模板

https://www.luogu.org/problem/P3804
求子串的出现次数。用类似拓扑排序的思想,从没有出度的节点开始把他的cnt加在他的后缀连接上。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

struct Node {
    int len, link, ch[26];
    //额外信息
    int cnt, head;
    void NewNode(int l = 0) {
        len = l, memset(ch, 0, sizeof(ch)), link = 0;
        cnt = 1, head = 0;
    }
    void CopyNewNode(const int &l, const Node& n) {
        len = l, memcpy(ch, n.ch, sizeof(ch)), link = n.link;
        cnt = 0, head = 0;
    }
};

const int MAXN = 1000000;

struct Edge {
    int to, next;
} edge[(MAXN << 1) + 5];
int etop;

//SuffixAutomaton
struct SAM {
    Node nd[(MAXN << 1) + 5];
    int top, last;  //top为节点个数,last末尾节点的位置

    void init() {
        nd[1].NewNode();
        top = 1, last = 1;
    }

    void extend(char ch) {
        int c = ch - 'a';
        int p = last, x = last = ++top;
        nd[x].NewNode(nd[p].len + 1);
        for(; p && !nd[p].ch[c]; p = nd[p].link)
            nd[p].ch[c] = x;
        if(!p)
            nd[x].link = 1;
        else {
            int q = nd[p].ch[c];
            if(nd[p].len + 1 == nd[q].len)
                nd[x].link = q;
            else {
                int y = ++top;
                nd[y].CopyNewNode(nd[p].len + 1, nd[q]);
                nd[q].link = nd[x].link = y;
                for(; p && nd[p].ch[c] == q; p = nd[p].link)
                    nd[p].ch[c] = y;
            }
        }
    }

    void dfs(int id) {
        for(int i = nd[id].head; i; i = edge[i].next) {
            dfs(edge[i].to);
            nd[id].cnt += nd[edge[i].to].cnt;
        }
    }

    ll solve() {
        //计算每一种子串的数量
        etop = 0;
        for(int i = top; i >= 2; --i) {
            ++etop;
            edge[etop].to = i;
            edge[etop].next = nd[nd[i].link].head;
            nd[nd[i].link].head = etop;
        }
        dfs(1);
        ll res = 0;
        for(int i = top; i >= 2; --i)
            if(nd[i].cnt > 1) {
                ll tmp = 1ll * nd[i].cnt * nd[i].len;
                if(tmp > res)
                    res = tmp;
            }
        return res;
    }
} sam;

char s[MAXN + 5];

int main() {
#ifdef Yinku
    freopen("Yinku.in", "r", stdin);
#endif // Yinku
    scanf("%s", s);
    sam.init();
    for(int i = 0; s[i] != ''; ++i)
        sam.extend(s[i]);
    printf("%lld
", sam.solve());
}

这个link树不如回文机优美,但是可以用拓扑排序来非递归求。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

struct Node {
    int len, link, ch[26];
    //额外信息
    int cnt;
    void NewNode(int l = 0) {
        len = l, memset(ch, 0, sizeof(ch)), link = 0;
        cnt = 1;
    }
    void CopyNewNode(const int &l, const Node& n) {
        len = l, memcpy(ch, n.ch, sizeof(ch)), link = n.link;
        cnt = 0;
    }
};

const int MAXN = 1000000;

//SuffixAutomaton
struct SAM {
    Node nd[(MAXN << 1) + 5];
    int top, last;  //top为节点个数,last末尾节点的位置

    void init() {
        nd[1].NewNode();
        top = 1, last = 1;
    }

    void extend(char ch) {
        int c = ch - 'a';
        int p = last, x = last = ++top;
        nd[x].NewNode(nd[p].len + 1);
        for(; p && !nd[p].ch[c]; p = nd[p].link)
            nd[p].ch[c] = x;
        if(!p)
            nd[x].link = 1;
        else {
            int q = nd[p].ch[c];
            if(nd[p].len + 1 == nd[q].len)
                nd[x].link = q;
            else {
                int y = ++top;
                nd[y].CopyNewNode(nd[p].len + 1, nd[q]);
                nd[q].link = nd[x].link = y;
                for(; p && nd[p].ch[c] == q; p = nd[p].link)
                    nd[p].ch[c] = y;
            }
        }
    }

    int d[(MAXN << 1) + 5];
    int que[(MAXN << 1) + 5], front, back;

    ll solve() {
        //计算每一种子串的数量
        memset(d, 0, sizeof(d));
        front = 1, back = 0;
        for(int i = top; i >= 2; --i)
            ++d[nd[i].link];
        for(int i = top; i >= 2; --i)
            if(!d[i])
                que[++back] = i;
        while(back >= front) {
            int u = que[front], v = nd[que[front]].link;
            nd[v].cnt += nd[u].cnt;
            --d[v];
            if(!d[v])
                que[++back] = v;
            ++front;
        }
        ll res = 0;
        for(int i = top; i >= 2; --i)
            if(nd[i].cnt > 1) {
                ll tmp = 1ll * nd[i].cnt * nd[i].len;
                if(tmp > res)
                    res = tmp;
            }
        return res;
    }
} sam;

char s[MAXN + 5];

int main() {
#ifdef Yinku
    freopen("Yinku.in", "r", stdin);
#endif // Yinku
    scanf("%s", s);
    sam.init();
    for(int i = 0; s[i] != ''; ++i)
        sam.extend(s[i]);
    printf("%lld
", sam.solve());
}

https://vjudge.net/contest/31226#problem/A
两个字符串ST的最长公共子串,对S构造SAM。然后逐个比较T的前缀。其实是逐个加入T的字符c,每次都尝试往后走到c,成功则len+1,否则退后到他的后缀连接上,同时更新len,直到没有办法继续后缀连接则这个字符彻底失配。比dp解法快多了。

记得要偏移’a'就都偏移'a',别演戏。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

struct Node {
    int len, link;
    int ch[26];
    //额外信息
    //int cnt;
    void NewNode(int l = 0) {
        len = l, link = 0;
        memset(ch, 0, sizeof(ch));
        //cnt = 1;
    }
    void CopyNewNode(const int &l, const Node& n) {
        len = l, link = n.link;
        memcpy(ch, n.ch, sizeof(ch));
        //cnt = 0;
    }
};

const int MAXN = 250000;

//SuffixAutomaton
struct SAM {
    Node nd[(MAXN << 1) + 5];
    int top, last;  //top为节点个数,last末尾节点的位置

    void init() {
        nd[1].NewNode();
        top = 1, last = 1;
    }

    void extend(char ch) {
        int c = ch - 'a';
        int p = last, x = last = ++top;
        nd[x].NewNode(nd[p].len + 1);
        for(; p && !nd[p].ch[c]; p = nd[p].link)
            nd[p].ch[c] = x;
        if(!p)
            nd[x].link = 1;
        else {
            int q = nd[p].ch[c];
            if(nd[p].len + 1 == nd[q].len)
                nd[x].link = q;
            else {
                int y = ++top;
                nd[y].CopyNewNode(nd[p].len + 1, nd[q]);
                nd[q].link = nd[x].link = y;
                for(; p && nd[p].ch[c] == q; p = nd[p].link)
                    nd[p].ch[c] = y;
            }
        }
    }

    int LongestCommonSubstring(char *t) {
        int ans = 0;
        int u = 1, len = 0;
        int n = strlen(t);
        int endpos = 0;
        for(int i = 0; i < n; ++i) {
            int c = t[i] - 'a';
            while(!nd[u].ch[c] && nd[u].link) {
                u = nd[u].link;
                len = nd[u].len;
            }
            if(nd[u].ch[c]) {
                u = nd[u].ch[c];
                ++len;
                if(len > ans) {
                    ans = len;
                    endpos = i;
                }
            }

        }
        /*for(int i = endpos - ans + 1; i <= endpos; ++i)
            putchar(t[i]);
        putchar('
');*/
        return ans;
    }

} sam;

char s[MAXN + 5], t[MAXN + 5];

int main() {
#ifdef Yinku
    freopen("Yinku.in", "r", stdin);
#endif // Yinku
    scanf("%s%s", s, t);
    sam.init();
    for(int i = 0; s[i] != ''; ++i)
        sam.extend(s[i]);
    printf("%d
", sam.LongestCommonSubstring(t));
}

多个字符串的LCS。又WA又T。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

const int INF = 0x3f3f3f3f;

struct Node {
    int len, link;
    int ch[26];
    void NewNode(int l = 0) {
        len = l, link = 0;
        //memset(ch, 0, sizeof(ch));
    }
    void CopyNewNode(const int &l, const Node& n) {
        len = l, link = n.link;
        memcpy(ch, n.ch, sizeof(ch));
    }
};

const int MAXLEN = 100000;
const int MAXNODE = 2 * MAXLEN + 5;

//SuffixAutomaton
struct SAM {
    Node nd[MAXNODE];
    int top, last;  //top为节点个数,last末尾节点的位置

    int minlen[MAXNODE], curlen[MAXNODE];

    void init() {
        nd[1].NewNode();
        top = 1, last = 1;
        memset(minlen, INF, sizeof(minlen));
    }

    void extend(char ch) {
        int c = ch - 'a';
        int p = last, x = last = ++top;
        nd[x].NewNode(nd[p].len + 1);
        for(; p && !nd[p].ch[c]; p = nd[p].link)
            nd[p].ch[c] = x;
        if(!p)
            nd[x].link = 1;
        else {
            int q = nd[p].ch[c];
            if(nd[p].len + 1 == nd[q].len)
                nd[x].link = q;
            else {
                int y = ++top;
                nd[y].CopyNewNode(nd[p].len + 1, nd[q]);
                nd[q].link = nd[x].link = y;
                for(; p && nd[p].ch[c] == q; p = nd[p].link)
                    nd[p].ch[c] = y;
            }
        }
    }

    int d[MAXNODE];
    int que[MAXNODE], front, back;

    void updateLink() {
        //别忘记沿着link更新各位祖先的长度
        front = 1, back = 0;
        for(int i = top; i >= 1; --i)
            ++d[nd[i].link];
        for(int i = top; i >= 1; --i)
            if(!d[i])
                que[++back] = i;
        while(back >= front) {
            int u = que[front++], v = nd[u].link;

            //它的link祖先的LCS长度不能超过他自己的长度
            int t = curlen[u] < nd[v].len ? curlen[u] : nd[v].len;
            if(t > curlen[v])
                curlen[v] = t;

            if(minlen[u] > curlen[u])
                minlen[u] = curlen[u];
            curlen[u] = 0;

            if(!(--d[v]))
                que[++back] = v;
        }
    }

    void LongestCommonSubstringHelp(char *t) {
        int u = 1, len = 0;
        for(int i = 0; t[i]; ++i) {
            int c = t[i] - 'a';
            while(!nd[u].ch[c] && nd[u].link) {
                u = nd[u].link;
                len = nd[u].len;
            }

            if(nd[u].ch[c]) {
                u = nd[u].ch[c];
                ++len;

                if(len > curlen[u])
                    curlen[u] = len;
            }
        }

        updateLink();
        return;
    }

    int LongestCommonSubstring() {
        int ans = 0;
        for(int i = top; i >= 1; --i)
            ans = max(ans, minlen[i]);
        return ans;
    }

} sam;

char s[MAXLEN], t[MAXLEN];

int main() {
#ifdef Yinku
    freopen("Yinku.in", "r", stdin);
#endif // Yinku
    scanf("%s", s);
    sam.init();
    for(int i = 0; s[i] != ''; ++i)
        sam.extend(s[i]);
    while(~scanf("%s", t))
        sam.LongestCommonSubstringHelp(t);

    printf("%d
", sam.LongestCommonSubstring());

}
原文地址:https://www.cnblogs.com/Yinku/p/11257552.html