• BZOJ2946 [Poi2000]公共串 【后缀自动机】


    题目

       给出几个由小写字母构成的单词,求它们最长的公共子串的长度。
    

    任务:
    l 读入单词
    l 计算最长公共子串的长度
    l 输出结果

    输入格式

       文件的第一行是整数 n,1<=n<=5,表示单词的数量。接下来n行每行一个单词,只由小写字母组成,单词的长度至少为1,最大为2000。
    

    输出格式

    仅一行,一个整数,最长公共子串的长度。
    

    输入样例

    3

    abcb

    bca

    acbc

    输出样例

    2

    题解

    经典的SAM求多串LCP
    首先对第一串建后缀自动机,然后用剩余的每一个串都在后缀自动机上跑一遍,分别得到后缀自动机上每个点的最大匹配长度
    最后每个点取每个串匹配长度的最小值,最大的那个点就是答案

    但要注意的是在parent树中,儿子的最大匹配值要向父亲传递,因为儿子所代表的位置集合是其父亲的真子集,父亲当然要包括儿子

    #include<iostream>
    #include<cmath>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #define LL long long int
    #define REP(i,n) for (int i = 1; i <= (n); i++)
    #define Redge(u) for (int k = h[u],to; k; k = ed[k].nxt)
    #define BUG(s,n) for (int i = 1; i <= (n); i++) cout<<s[i]<<' '; puts("");
    using namespace std;
    const int maxn = 4005,maxm = 100005,INF = 1000000000;
    int pre[maxn],ch[maxn][26],step[maxn],cnt,last,n;
    int g[maxn],f[maxn],a[maxn],b[maxn];
    char s[maxn];
    void ins(int x){
    	int p = last,np = ++cnt;
    	last = np; step[np] = step[p] + 1;
    	while (p && !ch[p][x]) ch[p][x] = np,p = pre[p];
    	if (!p) pre[np] = 1;
    	else {
    		int q = ch[p][x];
    		if (step[q] == step[p] + 1) pre[np] = q;
    		else {
    			int nq = ++cnt; step[nq] = step[p] + 1;
    			memcpy(ch[nq],ch[q],sizeof(ch[q]));
    			pre[nq] = pre[q]; pre[q] = pre[np] = nq;
    			while (ch[p][x] == q) ch[p][x] = nq,p = pre[p];
    		}
    	}
    }
    void topu(){
    	REP(i,cnt) b[step[i]]++;
    	REP(i,cnt) b[i] += b[i - 1];
    	REP(i,cnt) a[b[step[i]]--] = i;
    	REP(i,cnt) f[i] = step[i];
    }
    void walk(){
    	scanf("%s",s + 1); n = strlen(s + 1);
    	int u = 1,ans = 0,id;
    	REP(i,cnt) g[i] = 0;
    	for (int i = 1; i <= n; i++){
    		id = s[i] - 'a';
    		if (ch[u][id]) u = ch[u][id],g[u] = max(g[u],++ans);
    		else {
    			while (u != 1 && !ch[u][id]) u = pre[u];
    			if (u == 1) ans = 0;
    			else ans = step[u] + 1,u = ch[u][id],g[u] = max(g[u],ans);
    		}
    	}
    	for (int i = cnt; i; i--){
    		u = a[i];
    		if (pre[u]) g[pre[u]] = max(g[pre[u]],g[u]);
    		if (f[u] > g[u]) f[u] = g[u];
    	}
    }
    int main(){
    	int N; scanf("%d%s",&N,s + 1); N--;
    	cnt = last = 1; n = strlen(s + 1);
    	REP(i,n) ins(s[i] - 'a');
    	topu();
    	while (N--) walk();
    	int ans = 0;
    	REP(i,cnt) ans = max(ans,f[i]);
    	printf("%d
    ",ans);
    	return 0;
    }
    
    
  • 相关阅读:
    using 关键字在 C++ 中的几种用法
    Chromium 修改图片资源
    SAM&广义SAM
    Burnside和Polya
    笔记:杜教筛
    笔记:莫比乌斯反演
    Miller-Rabin
    点分治
    虚树
    计算几何
  • 原文地址:https://www.cnblogs.com/Mychael/p/8311118.html
Copyright © 2020-2023  润新知