POJ 3415 Life Forms 给定n个字符串,求出现在不小于k个字符串中的最长子串。
2017-10-01 17:17
441 查看
这个题要用到手工模拟的单调栈。首先这个题如果暴力应该怎么解决。例如:
2aaa
Aaaa
中间加个z
他的高度数组应该是
0 ‘\0’
0 a
1 aa
2 aaa
3 aaaa
3 aaazaaaa
2 aazaaaa
1 azaaaa
0 zaaaa
前缀大于2的在一个字符串里面的不能计算我们从上面可以看出
aaazaaaa和aa,aaa,aaaa分别进行匹配,aazaaaa和aa,aaa,aaaa分别进行匹配
前缀。一共是8个。如果进行暴力,应该是接近n*n不进行暴力,手动模拟单调栈,O(n)的算法。就是看当前串和前一个串能匹配多少个。不太好写具体怎么一步步走的,只能看着代码自己理解了。
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<cmath> #define N 100005 #define LL long long #define maxn 200005 using namespace std; //以下为倍增算法求后缀数组 int wa[maxn],wb[maxn],wv[maxn],Ws[maxn]; int cmp(int *r,int a,int b,int l) {return r[a]==r[b]&&r[a+l]==r[b+l];} void da(const char *r,int *sa,int n,int m){ int i,j,p,*x=wa,*y=wb,*t; for(i=0;i<m;i++) Ws[i]=0; for(i=0;i<n;i++) Ws[x[i]=r[i]]++; for(i=1;i<m;i++) Ws[i]+=Ws[i-1]; for(i=n-1;i>=0;i--) sa[--Ws[x[i]]]=i; for(j=1,p=1;p<n;j*=2,m=p){ for(p=0,i=n-j;i<n;i++) y[p++]=i; for(i=0;i<n;i++) if(sa[i]>=j) y[p++]=sa[i]-j; for(i=0;i<n;i++) wv[i]=x[y[i]]; for(i=0;i<m;i++) Ws[i]=0; for(i=0;i<n;i++) Ws[wv[i]]++; for(i=1;i<m;i++) Ws[i]+=Ws[i-1]; for(i=n-1;i>=0;i--) sa[--Ws[wv[i]]]=y[i]; for(t=x,x=y,y=t,p=1,x[sa[0]]=0,i=1;i<n;i++) x[sa[i]]=cmp(y,sa[i-1],sa[i],j)?p-1:p++; } return; } int sa[maxn],Rank[maxn],height[maxn]; //求height数组 void calheight(const char *r,int *sa,int n){ int i,j,k=0; for(i=1;i<=n;i++) Rank[sa[i]]=i; for(i=0;i<n;height[Rank[i++]]=k) for(k?k--:0,j=sa[Rank[i]-1];r[i+k]==r[j+k];k++); return; } char str[maxn],ch[maxn]; int k; int s[maxn][2]; LL tot,top; int main(){ while(scanf("%d",&k)!=EOF&&k){ int l1,l2; scanf("%s%s",str,ch); l1=strlen(str);l2=strlen(ch); str[l1]='@'; for(int i=l1+1;i<=l1+l2;i++) str[i]=ch[i-l1-1]; int n=l1+l2+1; str ='\0'; da(str,sa,n+1,130); calheight(str,sa,n); tot=top=0; LL sum=0; for(int i=1;i<=n;i++){ if(height[i]<k) top=tot=0; else{ int cnt=0; if(sa[i-1]<l1) cnt++,tot+=height[i]-k+1; while(top>0&&height[i]<=s[top-1][0]){ top--; tot-=s[top][1]*(s[top][0]-height[i]); cnt+=s[top][1]; } s[top][0]=height[i];s[top++][1]=cnt; if(sa[i]>l1) sum+=tot; } } tot=top=0; for(int i=1;i<=n;i++){ if(height[i]<k) top=tot=0; else{ int cnt=0; if(sa[i-1]>l1) cnt++,tot+=height[i]-k+1; while(top>0&&height[i]<=s[top-1][0]){ top--; tot-=s[top][1]*(s[top][0]-height[i]); cnt+=s[top][1]; } s[top][0]=height[i];s[top++][1]=cnt; if(sa[i]<l1) sum+=tot; } } printf("%I64d\n",sum); } return 0; }
相关文章推荐
- POJ 3294 Life Forms (后缀数组,求出现在不少于k个字符串的最长子串)
- POJ 3294 Life Forms(不小于k个字符串中的最长子串 后缀数组)
- 【POJ 3294】Life Forms 不小于k个字符串中的最长子串
- POJ 3294 Life Forms (后缀数组,求出现在不少于k个字符串的最长子串)
- Life Forms 后缀数组 不小于k个字符串中的最长子串
- POJ 3294 后缀数组:求不小于k个字符串中的最长子串
- POJ-3294-Life Forms(后缀数组-不小于 k 个字符串中的最长子串)
- POJ 3294 Life Forms 后缀数组+二分 求至少k个字符串中包含的最长子串
- poj 3294 在K个字符串中出现最长公共子串
- poj 3294 Life Forms(不小于k 个字符串中的最长子串)
- POJ 3294 Life Forms(后缀数组求k个串的最长子串)
- poj3294(不小于k个字符串中的最长子串---后缀数组)
- POJ3294 出现在至少k个字符串中的最长子串
- UVa 11107 生命的形式(不小于k个字符串中的最长子串)
- POJ3294--Life Forms 后缀数组+二分答案 大于k个字符串的最长公共子串
- POJ 3415 求两个字符串间长度不小于k的公共子串的个数
- poj 3294 Life Forms 求n(n>1)个字符串的最长的一个子串 后缀数组
- poj 3415 后缀数组 两个字符串中长度不小于 k 的公共子串的个数
- POJ 3294 出现在至少K个字符串中的子串
- poj 3294 不小于 k 个字符串中的最长子串(后缀数组+二分)