• ●后缀数组○十三个例题


    ●之前学习过后缀数组的倍增算法,但也只是简单练了练倍增(O(n ㏒ n))。

    ●如今再次开始后缀,借助罗穗骞的论文《后缀数组——处理字符串的有力工具》,练习了论文里那十三个例题,学习了里面所包含的后缀数组处理字符串的应用。

    ●感觉收获不少,后缀倍增+后缀应用的代码能力提高了不少。

    ●于此发上各题方法的小总结,AC代码,以及除倍增算法外的时间复杂度,以此记录。

    ●后缀数组的应用

    ●一个字符串

    ○1、求字符串的字串个数。(SPOJ 694 SPOJ 705

    方法:一个串中不同子串的总数=∑(len-height[i]-sa[i])

    复杂度: O(n)

    代码:

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #define MAXN 50005
    using namespace std;
    char s[MAXN];
    int sa[MAXN],rank[MAXN],height[MAXN];
    int wa[MAXN],wy[MAXN],c[MAXN];
    bool cmp(int *y,int i,int k,int n)
    {
    	int aa=y[sa[i]],bb=y[sa[i-1]];
    	int cc=sa[i]+k<n?y[sa[i]+k]:0,dd=sa[i-1]+k<n?y[sa[i-1]+k]:0;
    	return aa==bb&&cc==dd;
    }
    void build(int n,int m)
    {
    	int *x=wa,*y=wy,i;
    	for(i=0;i<m;i++) c[i]=0;
    	for(i=0;i<n;i++) c[x[i]=s[i]]++;
    	for(i=1;i<m;i++) c[i]+=c[i-1];
    	for(i=n-1;i>=0;i--) sa[--c[x[i]]]=i;
    	for(int k=1;k<=n;k<<=1)
    	{
    		int p=0;
    		for(i=n-k;i<n;i++) y[p++]=i;
    		for(i=0;i<n;i++) if(sa[i]>=k) y[p++]=sa[i]-k;
    		for(i=0;i<m;i++) c[i]=0;
    		for(i=0;i<n;i++) c[x[y[i]]]++;
    		for(i=1;i<m;i++) c[i]+=c[i-1];
    		for(i=n-1;i>=0;i--) sa[--c[x[y[i]]]]=y[i];
    		m=2; swap(x,y); x[sa[0]]=1;
    		for(i=1;i<n;i++)
    			x[sa[i]]=cmp(y,i,k,n)?m-1:m++;
    		if(m>n) break;
    	}
    	for(i=0;i<n;i++) rank[sa[i]]=i;
    	int h=0;
    	for(i=0;i<n;i++)
    	{
    		if(h) h--;
    		if(rank[i]==0) continue;
    		int j=sa[rank[i]-1];
    		while(s[i+h]==s[j+h]) h++;
    		height[rank[i]]=h;
    	}
    }
    long long solve(int n)
    {
    	long long ans=0;
    	for(int i=0;i<n;i++) ans+=n-sa[i]-height[i];
    	return ans;
    }
    int main() 
    {
    	int k,n;scanf("%d",&k);
    	while(k--)
    	{
    		scanf("%s",s);
    		n=strlen(s);
    		build(n,300);
    		long long ans=solve(n);
    		printf("%I64d
    ",ans);
    	}
    	return 0;
    }
    

      

    ○2、询问后缀LCP

    方法:height[ ]构建ST表,然后RMQ

    复杂度:预处理 O(n ㏒ n) 询问 O(1)

    ○3、可重叠最长重复子串

    方法:max(height[ ])

    复杂度:O(n)

    ○4、不可重叠最长重复子串(POJ 1743

    方法:二分+height 分组

    复杂度:O(n ㏒ n)

    代码:

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #define MAXN 20005 
    using namespace std;
    int s[MAXN];
    int sa[MAXN],rank[MAXN],height[MAXN];
    int wa[MAXN],wy[MAXN],c[MAXN];
    int mi=0x3f3f3f3f;
    bool cmp(int *y,int i,int k,int n)
    {
    	int aa=y[sa[i]],bb=y[sa[i-1]];
    	int cc=sa[i]+k<n?y[sa[i]+k]:0,dd=sa[i-1]+k<n?y[sa[i-1]+k]:0;
    	return aa==bb&&cc==dd;
    }
    void build(int m,int n)
    {
    	int *x=wa,*y=wy,i;
    	for(i=0;i<m;i++) c[i]=0;
    	for(i=0;i<n;i++) c[x[i]=s[i]-mi+1]++;
    	for(i=1;i<m;i++) c[i]+=c[i-1];
    	for(i=n-1;i>=0;i--) sa[--c[x[i]]]=i;
    	for(int k=1;k<=n;k<<=1)
    	{
    		int p=0;
    		for(i=n-k;i<n;i++) y[p++]=i;
    		for(i=0;i<n;i++) if(sa[i]>=k) y[p++]=sa[i]-k;
    		for(i=0;i<m;i++) c[i]=0;
    		for(i=0;i<n;i++) c[x[y[i]]]++;
    		for(i=1;i<m;i++) c[i]+=c[i-1];
    		for(i=n-1;i>=0;i--) sa[--c[x[y[i]]]]=y[i];
    		m=1; swap(x,y); x[sa[0]]=0;
    		for(i=1;i<n;i++)
    			x[sa[i]]=cmp(y,i,k,n)?m-1:m++;
    		if(m>=n) break;
    	}
    	for(i=0;i<n;i++) rank[sa[i]]=i;
    	int h=0;
    	for(i=0;i<n;i++)
    	{
    		if(h) h--;
    		if(rank[i]==0) continue;
    		int j=sa[rank[i]-1];
    		while(s[i+h]==s[j+h]) h++;
    		height[rank[i]]=h;
    	}
    } 
    bool check(int x,int n)
    {
    	int ml=0x3f3f3f3f,mr=-0x3f3f3f3f;
    	for(int i=1;i<n+1;i++)
    	{
    		if(i<=n&&height[i]>=x) ml=min(ml,min(sa[i-1],sa[i])),mr=max(mr,max(sa[i-1],sa[i]));
    		else
    		{
    			if(mr-ml>=x) return 1;
    			ml=0x3f3f3f3f; mr=-0x3f3f3f3f;
    		}
    	}
    	return 0;
    }
    int solve(int n)
    {
    	int l=4,r=n/2-1,ans=0,mid;
    	while(l<=r)
    	{
    		mid=(l+r)/2;
    		if(check(mid,n-1)) ans=mid,l=mid+1;
    		else r=mid-1;
    	}
    	return ans;
    }
    int main() 
    {
    	while(1)
    	{
    		int x,y,n;
    		scanf("%d
    ",&n);
    		if(n==0) break;
    		scanf("%d",&y);
    		for(int i=1;i<n;i++)
    		{ 
    			scanf("%d",&x);
    			s[i-1]=x-y;
    			y=x;
    			mi=min(mi,s[i-1]);
    		}
    		if(n<10) {printf("0
    "); continue;}
    		build(200,n-1);
    		int ans=solve(n);
    		printf("%d
    ",ans+1);
    	} 
    	return 0;
    }
    

      

    ○5、可重叠至少出现k次的最长重复子串(POJ 3261

    方法:二分+height 分组

    复杂度:O(n ㏒ n)

    代码:

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #define MAXN 20005 
    using namespace std;
    int s[MAXN],xx[MAXN];
    int sa[MAXN],rank[MAXN],height[MAXN];
    int wa[MAXN],wy[MAXN],c[MAXN];
    bool cmp(int *y,int i,int k,int n)
    {
    	int aa=y[sa[i]],bb=y[sa[i-1]];
    	int cc=sa[i]+k<n?y[sa[i]+k]:0,dd=sa[i-1]+k<n?y[sa[i-1]+k]:0;
    	return aa==bb&&cc==dd;
    }
    void build(int n,int m)
    {
    	int *x=wa,*y=wy,i;
    	for(i=0;i<m;i++) c[i]=0;
    	for(i=0;i<n;i++) c[x[i]=s[i]]++;
    	for(i=1;i<m;i++) c[i]+=c[i-1];
    	for(i=n-1;i>=0;i--) sa[--c[x[i]]]=i;
    	for(int k=1;k<=n;k<<=1)
    	{
    		int p=0;
    		for(i=n-k;i<n;i++) y[p++]=i;
    		for(i=0;i<n;i++) if(sa[i]>=k) y[p++]=sa[i]-k;
    		for(i=0;i<m;i++) c[i]=0;
    		for(i=0;i<n;i++) c[x[y[i]]]++;
    		for(i=1;i<m;i++) c[i]+=c[i-1];
    		for(i=n-1;i>=0;i--) sa[--c[x[y[i]]]]=y[i];
    		m=2; swap(x,y); x[sa[0]]=1;
    		for(i=1;i<n;i++)
    			x[sa[i]]=cmp(y,i,k,n)?m-1:m++;
    		if(m>n) break;
    	}
    	for(i=0;i<n;i++) rank[sa[i]]=i;
    	int h=0;
    	for(i=0;i<n;i++)
    	{
    		if(h) h--;
    		if(rank[i]==0) continue;
    		int j=sa[rank[i]-1];
    		while(s[i+h]==s[j+h]) h++;
    		height[rank[i]]=h;
    	}
    }
    bool check(int n,int x,int k)
    {
    	int cnt=1;
    	for(int i=1;i<=n;i++)
    	{
    		if(i<n&&height[i]>=x) cnt++;
    		else
    		{
    			if(cnt>=k) return 1;
    			cnt=1;
    		}
    	}
    	return 0;
    }
    int solve(int n,int k)
    {
    	int l=1,r=n-k+1,ans=0,mid;
    	while(l<=r)
    	{
    		mid=(l+r)/2;
    		if(check(n,mid,k)) ans=mid,l=mid+1;
    		else r=mid-1;
    	}
    	return ans;
    }
    int main() 
    {
    	int n,k;
    	scanf("%d%d",&n,&k);
    	for(int i=0;i<n;i++) scanf("%d",&s[i]),xx[i]=s[i];
    	sort(xx,xx+n); int d=unique(xx,xx+n)-xx;
    	for(int i=0;i<n;i++) s[i]=lower_bound(xx,xx+d,s[i])-xx+1;
    	build(n,d+1);
    	int ans=solve(n,k);
    	printf("%d",ans);
    	return 0;
    }
    

      

    ○6、连续重复子串(找原串的最短循环子串(循环节))(POJ 2406

    方法:

    后缀数组:穷举循环子串的长度L,判断RMQ(suffix(0),suffix(L))是否等于n-L。

    复杂度:O(n ㏒ n)

    但对于本题,数据范围过大,连倍增算法可能都不太跑得动。

    我看网上有用DC3的代码,有100多行。

    但感觉其实KMP才是王道,只有19行。

    代码(KMP的):

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    using namespace std;
    int next[1000005],tot;
    char s[1000005];
    int main()
    {
    	int k,j,n; next[0]=-1;
    	while(1)
    	{
    		scanf("%s",s); if(s[0]=='.') break;
    		n=strlen(s); k=-1; j=0;
    		while(j<n) if(k==-1||s[k]==s[j]) k++,j++,next[j]=k;else k=next[k];
    		if(!(n%(n-next[n]))) printf("%d
    ",n/(n-next[n]));
    		else printf("1
    ");
    	}	
    	return 0;
    } 
    

      

    ○7、重复最多的连续重复子串(POJ 3693 SPOJ 687

    方法:论文里的说,穷举L,i,RMQ(suffix(iL),suffix(iL+L))+”错位“判断

    但这个”错位“判断的代码实现真的令我绝望,况且POJ上还有求输出字典序最小的那个串。。。最后我还是学习的网上一个大佬的方法,但复杂度我不知如何证明。

    代码(POJ的,SPOJ的”简单版“我WA了,还不知道错在哪里):

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<cmath>
    #define MAXN 100005
    using namespace std;
    char s[MAXN];
    int sa[MAXN],rank[MAXN],height[MAXN];
    int wa[MAXN],wb[MAXN],c[MAXN];
    int st[MAXN][15],a[MAXN];
    int al,ap,ans,cnt;
    bool cmp(int *y,int i,int k,int n)
    {
    	int aa=y[sa[i]],bb=y[sa[i-1]];
    	int cc=sa[i]+k<n?y[sa[i]+k]:0,dd=sa[i-1]+k<n?y[sa[i-1]+k]:0;
    	return aa==bb&&cc==dd;
    }
    void build(int n,int m)
    {
    	int *x=wa,*y=wb,i;
    	for(i=0;i<m;i++) c[i]=0;
    	for(i=0;i<n;i++) c[x[i]=s[i]]++;
    	for(i=1;i<m;i++) c[i]+=c[i-1];
    	for(i=n-1;i>=0;i--) sa[--c[x[i]]]=i;
    	for(int k=1;k<=n;k<<=1)
    	{
    		int p=0;
    		for(i=n-k;i<n;i++) y[p++]=i;
    		for(i=0;i<n;i++) if(sa[i]>=k) y[p++]=sa[i]-k;
    		for(i=0;i<m;i++) c[i]=0;
    		for(i=0;i<n;i++) c[x[y[i]]]++;
    		for(i=1;i<m;i++) c[i]+=c[i-1];
    		for(i=n-1;i>=0;i--) sa[--c[x[y[i]]]]=y[i];
    		m=2; swap(x,y); x[sa[0]]=1;
    		for(i=1;i<n;i++)
    			x[sa[i]]=cmp(y,i,k,n)?m-1:m++;
    		if(m>n) break;
    	}
    	for(i=0;i<n;i++) rank[sa[i]]=i;
    	int h=0;
    	for(i=0;i<n;i++)
    	{
    		if(h) h--;
    		if(rank[i]==0) {height[rank[i]]=0;continue;}
    		int j=sa[rank[i]-1];
    		while(s[i+h]==s[j+h]) h++;
    		height[rank[i]]=h;
    	}
    }
    void pre_ST(int n)
    {
    	for(int i=1;i<n;i++) st[i][1]=height[i];
    	for(int j=2;(1<<j)<n;j++)
    		for(int i=(1<<j);i<n;i++)
    			st[i][j]=min(st[i-(1<<(j-1))+1][1],min(st[i-(1<<(j-1))][j-1],st[i][j-1]));
    }
    int rmq(int p1,int p2,int n)
    { 
    	if(p1>=n||p2>=n) return 0;
    	p1=rank[p1]; p2=rank[p2];
    	if(p1>p2) swap(p1,p2);
    	int k=log2(p2-p1+1);
    	return min(st[p1+(1<<k)-1][k],st[p2][k]);
    }
    void solve(int n)
    {
    	int now,k,p; ans=0,cnt=0;
    	for(int l=1;l<=n/2;l++)
    		for(int i=0;i+l<n;i+=l)
    		{
    			k=rmq(i,i+l,n);
    			now=k/l+1;
    			p=i-(l-k%l);
    			if(p>=0&&k%l!=0&&rmq(p,p+l,n)>=k) now++;
    			if(now>ans) cnt=0,a[++cnt]=l,ans=now;
    			else if(now==ans) a[++cnt]=l;
    		}
    	ap=0; al=0;
    	for(int i=0;i<n;i++)  //根据排名枚举,保证字典序最小 
    	for(int j=1;j<=cnt;j++)
    	{
    		k=rmq(sa[i],sa[i]+a[j],n);
    		if(k>=(ans-1)*a[j])
    		{
    			ap=sa[i];
    			al=ans*a[j];
    			i=n;
    			break;
    		}
    	}
    }
    int main() 
    {
    	int n,cas=1;
    	while(1)
    	{
    		scanf(" %s",s); if(s[0]=='#') break;
    		printf("Case %d: ",cas++);
    		n=strlen(s);
    		build(n,300);
    		pre_ST(n);
    		solve(n);
    		for(int i=ap;i<ap+al;i++) printf("%c",s[i]);
    		printf("
    ");
    	}
    	return 0;
    }
    

      

    ○8、最长回文串(ural 1297

    方法:原串和反转串拼凑(中间用未出现过的字符连接),枚举回文中心,RMQ对应位置的两个后缀的LCP,并分奇偶串讨论。

    复杂度:O(n ㏒ n)

    代码:

    #include<cmath>
    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #define MAXN 20005
    using namespace std;
    char s[MAXN];
    int sa[MAXN],rank[MAXN],height[MAXN];
    int wa[MAXN],wy[MAXN],c[MAXN];
    int st[MAXN][12];
    int ans,ap;
    bool cmp(int *y,int i,int k,int n)
    {
    	int aa=y[sa[i]],bb=y[sa[i-1]];
    	int cc=sa[i]+k<n?y[sa[i]+k]:0,dd=sa[i-1]+k<n?y[sa[i-1]+k]:0;
    	return aa==bb&&cc==dd;
    }
    void build(int n,int m)
    {
    	int *x=wa,*y=wy,i;
    	for(i=0;i<m;i++) c[i]=0;
    	for(i=0;i<n;i++) c[x[i]=s[i]]++;
    	for(i=1;i<m;i++) c[i]+=c[i-1];
    	for(i=n-1;i>=0;i--) sa[--c[x[i]]]=i;
    	for(int k=1;k<=n;k<<=1)
    	{
    		int p=0;
    		for(i=n-k;i<n;i++) y[p++]=i;
    		for(i=0;i<n;i++) if(sa[i]>=k) y[p++]=sa[i]-k;
    		for(i=0;i<m;i++) c[i]=0;
    		for(i=0;i<n;i++) c[x[y[i]]]++;
    		for(i=1;i<m;i++) c[i]+=c[i-1];
    		for(i=n-1;i>=0;i--) sa[--c[x[y[i]]]]=y[i];
    		m=2; swap(x,y); x[sa[0]]=1;
    		for(i=1;i<n;i++)
    			x[sa[i]]=cmp(y,i,k,n)?m-1:m++;
    		if(m>n) break;
    	}
    	for(i=0;i<n;i++) rank[sa[i]]=i;
    	int h=0;
    	for(i=0;i<n;i++)
    	{
    		if(h) h--;
    		if(rank[i]==0) continue;
    		int j=sa[rank[i]-1];
    		while(s[i+h]==s[j+h]) h++;
    		height[rank[i]]=h;
    	}
    }
    void pre_ST(int n)
    {
    	for(int i=1;i<n;i++) st[i][1]=height[i];
    	for(int j=2;(1<<j)<n;j++)
    		for(int i=(1<<j);i<n;i++)
    			st[i][j]=min(st[i-(1<<(j-1))+1][1],min(st[i-(1<<(j-1))][j-1],st[i][j-1]));
    }
    int rmq(int p1,int p2)
    {
    	p1=rank[p1]; p2=rank[p2];
    	if(p1>p2) swap(p1,p2);
    	int k=log2(p2-p1+1);
    	return min(st[p1+(1<<k)-1][k],st[p2][k]);
    }
    void solve(int n)
    {
    	int p1,p2,l;
    	ans=0,ap=-1;
    	for(p1=0;p1<n/2;p1++)
    	{
    		p2=n-1-p1;
    		//奇
    		l=rmq(p1,p2);
    		if(ans<(l-1)*2+1)
    		{
    			ans=(l-1)*2+1;
    			ap=p1-l+1;
    		}
    		//偶
    		l=rmq(p1,p2+1);
    		if(ans<l*2)
    		{
    			ans=l*2;
    			ap=p1-l;
    		}
    	}
    }
    int main() 
    {
    	int n,l;
    	scanf("%s",s);
    	l=strlen(s);s[l]=1;
    	n=2*l+1;
    	for(int i=0;i<l;i++) s[n-1-i]=s[i];
    	build(n,300);
    	pre_ST(n);
    	solve(n);
    	if(ans) for(int i=ap;i<=ap+ans-1;i++) printf("%c",s[i]);
    	puts("");
    	return 0;
    }
    

      

    ●两个字符串

    ○9、最长公共子串(POJ 2774

    方法:拼凑+max(height[ ]),且height[i]对应的sa[i]和sa[i-1]位于不同串。

    复杂度:O(n)

    代码:

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #define MAXN 200005 
    using namespace std;
    char s[MAXN];
    int sa[MAXN],rank[MAXN],height[MAXN];
    int wa[MAXN],wy[MAXN],c[MAXN];
    bool cmp(int *y,int i,int k,int n)
    {
    	int aa=y[sa[i]],bb=y[sa[i-1]];
    	int cc=sa[i]+k<n?y[sa[i]+k]:0,dd=sa[i-1]+k<n?y[sa[i-1]+k]:0;
    	return aa==bb&&cc==dd;
    }
    void build(int n,int m)
    {
    	int *x=wa,*y=wy,i;
    	for(i=0;i<m;i++) c[i]=0;
    	for(i=0;i<n;i++) c[x[i]=s[i]]++;
    	for(i=1;i<m;i++) c[i]+=c[i-1];
    	for(i=n-1;i>=0;i--) sa[--c[x[i]]]=i;
    	for(int k=1;k<=n;k<<=1)
    	{
    		int p=0;
    		for(i=n-k;i<n;i++) y[p++]=i;
    		for(i=0;i<n;i++) if(sa[i]>=k) y[p++]=sa[i]-k;
    		for(i=0;i<m;i++) c[i]=0;
    		for(i=0;i<n;i++) c[x[y[i]]]++;
    		for(i=1;i<m;i++) c[i]+=c[i-1];
    		for(i=n-1;i>=0;i--) sa[--c[x[y[i]]]]=y[i];
    		m=2; swap(x,y); x[sa[0]]=1;
    		for(i=1;i<n;i++)
    			x[sa[i]]=cmp(y,i,k,n)?m-1:m++;
    		if(m>n) break;
    	}
    	for(i=0;i<n;i++) rank[sa[i]]=i;
    	int h=0;
    	for(i=0;i<n;i++)
    	{
    		if(h) h--;
    		if(rank[i]==0) continue;
    		int j=sa[rank[i]-1];
    		while(s[i+h]==s[j+h]) h++;
    		height[rank[i]]=h;
    	}
    }
    int solve(int n,int l)
    {
    	int ans=0;
    	for(int i=2;i<n;i++)
    		if((sa[i-1]<l)^(sa[i]<l)) ans=max(ans,height[i]);
    	return ans;
    }
    int main() 
    {
    	int n,l;
    	scanf("%s",s);
    	l=strlen(s); s[l]=1;
    	scanf("%s",s+l+1);
    	n=strlen(s);
    	build(n,300);
    	int ans=solve(n,l);
    	printf("%d",ans);
    	return 0;
    }
    

      

    ○10、长度不小于k的公共子串的个(对)数(有点不好理解题意)(POJ 3415

    方法:拼凑+按k对height[]进行分组+单调栈维护答案贡献

    复杂度:O(n)

    代码:

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #define MAXN 200002
    using namespace std;
    char s[MAXN];
    int sa[MAXN],rank[MAXN],height[MAXN];
    int wa[MAXN],wy[MAXN],c[MAXN];
    int a[MAXN],f[MAXN],b[MAXN];
    int n;
    long long ans,tmp;
    bool cmp(int *y,int i,int k,int n)
    {
    	int aa=y[sa[i]],bb=y[sa[i-1]];
    	int cc=sa[i]+k<n?y[sa[i]+k]:0,dd=sa[i-1]+k<n?y[sa[i-1]+k]:0;
    	return aa==bb&&cc==dd;
    }
    void build()
    {
    	int *x=wa,*y=wy,m=300,i;
    	for(i=0;i<m;i++) c[i]=0;
    	for(i=0;i<n;i++) c[x[i]=s[i]]++;
    	for(i=1;i<m;i++) c[i]+=c[i-1];
    	for(i=n-1;i>=0;i--) sa[--c[x[i]]]=i;
    	for(int k=1;k<n;k<<=1)
    	{
    		int p=0;
    		for(i=n-k;i<n;i++) y[p++]=i;
    		for(i=0;i<n;i++) if(sa[i]>=k) y[p++]=sa[i]-k;
    		for(i=0;i<m;i++) c[i]=0;
    		for(i=0;i<n;i++) c[x[y[i]]]++;
    		for(i=1;i<m;i++) c[i]+=c[i-1];
    		for(i=n-1;i>=0;i--) sa[--c[x[y[i]]]]=y[i];
    		m=2; swap(x,y); x[sa[0]]=1;
    		for(i=1;i<n;i++)
    			x[sa[i]]=cmp(y,i,k,n)?m-1:m++;
    		if(m>n) break;
    	}
    	for(i=0;i<n;i++) rank[sa[i]]=i;
    	int h=0;
    	for(i=0;i<n;i++)
    	{
    		if(h) h--;
    		if(rank[i]==0) continue;
    		int j=sa[rank[i]-1];
    		while(s[i+h]==s[j+h]) h++;
    		height[rank[i]]=h;
    	}
    }
    int main() 
    {
    	int k,l;
    	while(1)
    	{
    		scanf("%d",&k);
    		if(!k) break;
    		scanf("%s",s); l=strlen(s);
    		s[l]=1;
    		scanf("%s",s+l+1);
    		n=strlen(s);
    		build();
    		for(int i=1;i<n;i++)
    		{
    			f[i]=sa[i]<l;
    			height[i]-=k-1;
    			if(height[i]<0) height[i]=0;	
    		} 
    		a[0]=-1;
    		height[n]=0;
    		ans=0;
    		int p;
    		for(int t=0;t<=1;t++)
    		{
    			tmp=0;
    			p=0;
    			for(int i=1;i<n;i++)
    			{
    				if(f[i]!=t) ans+=tmp;
    				p++;
    				a[p]=height[i+1];
    				b[p]=f[i]==t;
    				tmp+=(long long)a[p]*b[p];
    				while(a[p-1]>=a[p])
    				{
    					tmp-=(long long)(a[p-1]-a[p])*b[p-1];
    					a[p-1]=a[p];
    					b[p-1]+=b[p];
    					p--;
    				}
    			}
    		}
    		printf("%I64d
    ",ans);
    		//cout<<ans<<endl;
    	}
    	return 0;
    }
    

      

    ●多个字符串(这三个都比较套路了,方法大同小异)

    ○11、出现在不少于k个字符串中(POJ 3294

    方法:拼凑各串+二分+height 分组

    复杂度:O(n ㏒ n)

    代码:

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #define MAXN 110000
    using namespace std;
    char ch;
    int s[MAXN],r[MAXN],a[MAXN];
    int sa[MAXN],rank[MAXN],height[MAXN];
    int wa[MAXN],wy[MAXN],c[MAXN];
    int vis[105];
    int t,cnt,tot,mi;
    bool cmp(int *y,int i,int k,int n)
    {
    	int aa=y[sa[i]],bb=y[sa[i-1]];
    	int cc=sa[i]+k<n?y[sa[i]+k]:0,dd=sa[i-1]+k<n?y[sa[i-1]+k]:0;
    	return aa==bb&&cc==dd;
    }
    void build(int n,int m)
    {
    	int *x=wa,*y=wy,i;
    	for(i=0;i<m;i++) c[i]=0;
    	for(i=0;i<n;i++) c[x[i]=s[i]]++;
    	for(i=1;i<m;i++) c[i]+=c[i-1];
    	for(i=n-1;i>=0;i--) sa[--c[x[i]]]=i;
    	for(int k=1;k<=n;k<<=1)
    	{
    		int p=0;
    		for(i=n-k;i<n;i++) y[p++]=i;
    		for(i=0;i<n;i++) if(sa[i]>=k) y[p++]=sa[i]-k;
    		for(i=0;i<m;i++) c[i]=0;
    		for(i=0;i<n;i++) c[x[y[i]]]++;
    		for(i=1;i<m;i++) c[i]+=c[i-1];
    		for(i=n-1;i>=0;i--) sa[--c[x[y[i]]]]=y[i];
    		m=1; swap(x,y); x[sa[0]]=0;
    		for(i=1;i<n;i++)
    			x[sa[i]]=cmp(y,i,k,n)?m-1:m++;
    		if(m>=n) break;
    	}
    	for(i=0;i<n;i++) rank[sa[i]]=i;
    	int h=0;
    	for(i=0;i<n;i++)
    	{
    		if(h) h--;
    		if(rank[i]==0) continue;
    		int j=sa[rank[i]-1];
    		while(s[i+h]==s[j+h]) h++;
    		height[rank[i]]=h;
    	}
    }
    bool check(int x,int n,int k)
    {
    	t++; cnt=1; vis[r[sa[0]]]=t; tot=0; bool fg=0;
    	for(int i=1;i<=n;i++)
    	{
    		if(i<n&&height[i]>=x) 
    		{
    			if(r[sa[i]]&&vis[r[sa[i]]]!=t) vis[r[sa[i]]]=t,cnt++;	
    		}
    		else
    		{
    			
    			if(cnt>=k) a[++tot]=sa[i-1],fg=1;
    			cnt=0;
    			t++;
    			if(r[sa[i]]&&vis[r[sa[i]]]!=t) vis[r[sa[i]]]=t,cnt++;
    			
    		}
    	}
    	return fg;
    }
    void solve(int n,int k)
    {
    	int l=1,r=mi,mid,ans=-1,ant;
    	while(l<=r)
    	{
    		mid=(l+r)/2;
    		if(check(mid,n,k)) ans=mid,ant=tot,l=mid+1;
    		else r=mid-1;
    	}
    	if(ans==-1){printf("?");return;}
    	for(int i=1;i<=ant;i++)
    	{
    		for(int j=a[i];j<a[i]+ans;j++) printf("%c",s[j]+'A');
    		if(i!=ant) printf("
    ");
    	}
    }
    int main() 
    {
    	int n,k,l; bool fg=0;
    	while(1)
    	{
    		scanf("%d",&k); char ss[1005]; mi=0x3f3f3f3f; n=0;
    		if(!k) break;
    		if(fg) printf("
    
    ");
    		if(!fg) fg=1;
    		for(int i=1;i<=k;i++)
    		{
    			scanf("%s",ss); l=strlen(ss); mi=min(mi,l);
    			for(int j=0;j<l;j++) s[n+j]=ss[j]-'A',r[n+j]=i;
    			n+=l; s[n]=i+200; r[n]=0; n++;
    		}
    		build(n,350);
    		solve(n,k/2+1);
    	}
    	return 0;
    }
    

      

    ○12、在每个字符串中不重复出现两次的最长子串(SPOJ 220

    方法:拼凑各串+二分+height 分组

    复杂度:O(n ㏒ n)

    代码:

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #define MAXN 110000
    using namespace std;
    char ch;
    int s[MAXN],r[MAXN],a[MAXN];
    int sa[MAXN],rank[MAXN],height[MAXN];
    int wa[MAXN],wy[MAXN],c[MAXN];
    int vis[2][15];
    int t,cnt,tot,mi;
    bool cmp(int *y,int i,int k,int n)
    {
    	int aa=y[sa[i]],bb=y[sa[i-1]];
    	int cc=sa[i]+k<n?y[sa[i]+k]:0,dd=sa[i-1]+k<n?y[sa[i-1]+k]:0;
    	return aa==bb&&cc==dd;
    }
    void build(int n,int m)
    {
    	int *x=wa,*y=wy,i;
    	for(i=0;i<m;i++) c[i]=0;
    	for(i=0;i<n;i++) c[x[i]=s[i]]++;
    	for(i=1;i<m;i++) c[i]+=c[i-1];
    	for(i=n-1;i>=0;i--) sa[--c[x[i]]]=i;
    	for(int k=1;k<=n;k<<=1)
    	{
    		int p=0;
    		for(i=n-k;i<n;i++) y[p++]=i;
    		for(i=0;i<n;i++) if(sa[i]>=k) y[p++]=sa[i]-k;
    		for(i=0;i<m;i++) c[i]=0;
    		for(i=0;i<n;i++) c[x[y[i]]]++;
    		for(i=1;i<m;i++) c[i]+=c[i-1];
    		for(i=n-1;i>=0;i--) sa[--c[x[y[i]]]]=y[i];
    		m=1; swap(x,y); x[sa[0]]=0;
    		for(i=1;i<n;i++)
    			x[sa[i]]=cmp(y,i,k,n)?m-1:m++;
    		if(m>=n) break;
    	}
    	for(i=0;i<n;i++) rank[sa[i]]=i;
    	int h=0;
    	for(i=0;i<n;i++)
    	{
    		if(h) h--;
    		if(rank[i]==0) continue;
    		int j=sa[rank[i]-1];
    		while(s[i+h]==s[j+h]) h++;
    		height[rank[i]]=h;
    	}
    }
    bool check(int x,int n,int k)
    {
    	memset(vis[0],0x3f,sizeof(vis[0]));
    	memset(vis[1],-1,sizeof(vis[1]));
    	bool fg=0;
    	vis[0][r[sa[0]]]=min(vis[0][r[sa[0]]],sa[0]);
    	vis[1][r[sa[0]]]=max(vis[1][r[sa[0]]],sa[0]);
    	for(int i=1;i<=n;i++)
    	{
    		if(i<n&&height[i]>=x) 
    		{
    				vis[0][r[sa[i]]]=min(vis[0][r[sa[i]]],sa[i]);
    				vis[1][r[sa[i]]]=max(vis[1][r[sa[i]]],sa[i]);	
    		}
    		else
    		{
    			fg=1;
    			for(int j=1;j<=k;j++) 
    				if(vis[1][j]-vis[0][j]<x) {fg=0;break;}
    			if(fg) return 1;
    			memset(vis[0],0x3f,sizeof(vis[0]));
    			memset(vis[1],-1,sizeof(vis[1]));
    			vis[0][r[sa[i]]]=min(vis[0][r[sa[i]]],sa[i]);
    			vis[1][r[sa[i]]]=max(vis[1][r[sa[i]]],sa[i]);
    		}
    	}
    	return 0;
    }
    void solve(int n,int k)
    {
    	int l=1,r=mi/2,mid,ans=0;
    	while(l<=r)
    	{
    		mid=(l+r)/2;
    		if(check(mid,n,k)) ans=mid,l=mid+1;
    		else r=mid-1;
    	}
    	printf("%d
    ",ans);
    }
    void gs(char *ss)
    {
    	int p=0; ch=getchar();
    	while(ch<'a'||ch>'z') ch=getchar();
    	while('a'<=ch&&ch<='z') ss[p++]=ch,ch=getchar();
    	ss[p]=0;
    }
    int main() 
    {
    	int n,k,l,T; scanf("%d",&T);
    	while(T--)
    	{
    		scanf("%d",&k); char ss[10005]; mi=0x3f3f3f3f; n=0;
    		for(int i=1;i<=k;i++)
    		{
    			gs(ss),l=strlen(ss); mi=min(mi,l);
    			for(int j=0;j<l;j++) s[n+j]=ss[j]-'a',r[n+j]=i;
    			n+=l; s[n]=i+200; r[n]=0; n++;
    		}
    		build(n,350);
    		solve(n,k);
    	}
    	return 0;
    }
    

      

    ○13、出现在每个字符串或原串反转串中的最长子串(POJ 1226

    方法:拼凑各串及其反转串+二分+height 分组

    复杂度:O(n ㏒ n)

    代码:

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #define MAXN 30000
    using namespace std;
    int s[MAXN],r[MAXN],a[MAXN];
    int sa[MAXN],rank[MAXN],height[MAXN];
    int wa[MAXN],wy[MAXN],c[MAXN];
    bool vis[105];
    int t,cnt,tot,mi;char ch;
    bool cmp(int *y,int i,int k,int n)
    {
    	int aa=y[sa[i]],bb=y[sa[i-1]];
    	int cc=sa[i]+k<n?y[sa[i]+k]:0,dd=sa[i-1]+k<n?y[sa[i-1]+k]:0;
    	return aa==bb&&cc==dd;
    }
    void build(int n,int m)
    {
    	int *x=wa,*y=wy,i;
    	for(i=0;i<m;i++) c[i]=0;
    	for(i=0;i<n;i++) c[x[i]=s[i]]++;
    	for(i=1;i<m;i++) c[i]+=c[i-1];
    	for(i=n-1;i>=0;i--) sa[--c[x[i]]]=i;
    	for(int k=1;k<=n;k<<=1)
    	{
    		int p=0;
    		for(i=n-k;i<n;i++) y[p++]=i;
    		for(i=0;i<n;i++) if(sa[i]>=k) y[p++]=sa[i]-k;
    		for(i=0;i<m;i++) c[i]=0;
    		for(i=0;i<n;i++) c[x[y[i]]]++;
    		for(i=1;i<m;i++) c[i]+=c[i-1];
    		for(i=n-1;i>=0;i--) sa[--c[x[y[i]]]]=y[i];
    		m=1; swap(x,y); x[sa[0]]=0;
    		for(i=1;i<n;i++)
    			x[sa[i]]=cmp(y,i,k,n)?m-1:m++;
    		if(m>=n) break;
    	}
    	for(i=0;i<n;i++) rank[sa[i]]=i;
    	int h=0;
    	for(i=0;i<n;i++)
    	{
    		if(h) h--;
    		if(rank[i]==0) continue;
    		int j=sa[rank[i]-1];
    		while(s[i+h]==s[j+h]) h++;
    		height[rank[i]]=h;
    	}
    }
    bool check(int x,int n,int k)
    {
    	memset(vis,0,sizeof(vis));
    	bool fg=0;
    	for(int i=1;i<=n;i++)
    	{
    		if(i<n&&height[i]>=x) 
    		{
    			vis[r[sa[i]]]=1;
    		}
    		else
    		{
    			fg=1;
    			for(int j=1;j<=k;j++) 
    				if(!vis[j]) {fg=0;break;}
    			if(fg) return 1;
    			memset(vis,0,sizeof(vis));
    			vis[r[sa[i]]]=1;
    		}
    	}
    	return 0;
    }
    void solve(int n,int k)
    {
    	int l=1,r=mi,mid,ans=0;
    	while(l<=r)
    	{
    		mid=(l+r)/2;
    		if(check(mid,n,k)) ans=mid,l=mid+1;
    		else r=mid-1;
    	}
    	printf("%d
    ",ans);
    }
    int main()
    {
    	int n,k,l,T,o; scanf("%d",&T);
    	while(T--)
    	{
    		scanf("%d",&k); char ss[105]; mi=0x3f3f3f3f; n=0,o=200;
    		for(int i=1;i<=k;i++)
    		{
    			scanf("%s",ss);l=strlen(ss); mi=min(mi,l);
    			for(int j=0;j<l;j++) s[n+j]=ss[j],r[n+j]=i;
    			n+=l; s[n]=o++; r[n]=0; n++;
    			for(int j=0;j<l;j++) s[n+j]=ss[l-j-1],r[n+j]=i;
    			n+=l; s[n]=o++; r[n]=0; n++;
    		}
    		build(n,350);
    		solve(n,k);
    	}
    	return 0;
    }
    

      

  • 相关阅读:
    Java集合HashMap的实现原理(借鉴)
    Heritrix在eclipse中的配置
    Hadoop中涉及到的网络 I/O 优化
    李开复的博士论文人生思考
    Java中HastSet用法
    统计语言模型
    ASP.NET 中的正则表达式
    高ASP.Net应用程序性能的十大方法
    在多线程环境下使用HttpWebRequest或者调用Web Service
    使用TextBoxWatermark为TextBox加上水印效果
  • 原文地址:https://www.cnblogs.com/zj75211/p/7239754.html
Copyright © 2020-2023  润新知