题目链接:传送门
大致题意:现给定字符串s和t ,求s和t的长度不小于k的公共子串个数;
题目思路:
对于s的每一个后缀和t的每一个后缀求lcp,如果匹配出的lcp=x,那么ans+=x-k+1(x>=k),直接暴力显然不行,就有了下面的方法:
将两个串做连接得到字符串str,中间隔一个绝对不会出现的任意字符,比如‘#’,得到str = s + '#' + t 。
再对str求sa和height(加‘#’的目的是为了让height数组的值不超过s和t的长度。例如s=‘aaaaa’,t=‘aaaa’,直接连接的话,显然会使height的值过大,根据height[i]的定义是排名i和i-1的最长公共前缀)
lcp(i,j) = min ( height[i+1] , height[i+2] , ... , height[j] ) ;
对于排名为j且属于t串的后缀,直接求所有的lcp(i,j) 之和,其中i∈s且 i < j ,显然lcp(i,j) 与 lcp(i,j+1) 是有联系的(多了一个数字取min,lcp(i,j) <= lcp(i,j+1) ),因此就没必要重复计算
那么根据排名从小到大进行扫描,并且根据其height值维护一个单调栈(单增栈),单调栈的作用是维护[1,i-1] 中的s对 i 的贡献
另一种思路,可以直接用并查集来算,也是可行的;
#include<cstdio> #include<cstring> #include<ctype.h> #include<algorithm> #include<functional> #pragma GCC optimize(2) using namespace std; //std::mt19937 rnd(233); typedef long long LL; typedef pair<int,int> pii; typedef pair<LL,LL> pLL; #define pb push_back #define mk make_pair #define fi first #define se second #define ls (i<<1) #define rs (i<<1|1) #define mem(a,b) memset(a,b,sizeof(a)) const int N=1e6+5; const int inf=0x3f3f3f3f; const LL mod=1e9+7; LL read() { LL x=0,f=1; char ch=getchar(); while(!isdigit(ch)){ if(ch=='-') f=-1; ch=getchar(); } while(isdigit(ch)){ x=10*x+ch-'0'; ch=getchar(); } return f*x; } int sa[N],rk[N],x[N],y[N],he[N],c[N],n,m,sta[N],num[N]; char s[N],s1[N]; void SA() { int m=130; for(int i=1;i<=m;i++) c[i]=0; for(int i=1;i<=n;i++) c[x[i]=s[i]]++; for(int i=1;i<=m;i++) c[i]+=c[i-1]; for(int i=n;i;i--) sa[c[x[i]]--]=i; for(int k=1;k<=n;k<<=1) { int tot=0; for(int i=1;i<=m;i++) c[i]=0; for(int i=n-k+1;i<=n;i++) y[++tot]=i; for(int i=1;i<=n;i++) if(sa[i]>k) y[++tot]=sa[i]-k; for(int i=1;i<=n;i++) c[x[i]]++; for(int i=1;i<=m;i++) c[i]+=c[i-1]; for(int i=n;i;i--) sa[c[x[y[i]]]--]=y[i]; swap(x,y); tot=x[sa[1]]=1; for(int i=2;i<=n;i++) x[sa[i]]=(y[sa[i]]==y[sa[i-1]]&&y[sa[i]+k]==y[sa[i-1]+k])?tot:++tot; if(tot==n) break; m=tot; } } void gethe() { he[1]=0; for(int i=1;i<=n;i++) rk[sa[i]]=i; for(int i=1,k=0;i<=n;i++) { if(rk[i]==1) continue; if(k) k--; int j=sa[rk[i]-1]; while(i+k<=n&&j+k<=n&&s[i+k]==s[j+k]) k++; he[rk[i]]=k; } } int main() { int k; while(scanf("%d",&k)!=EOF&&k) { scanf("%s",s+1); scanf("%s",s1+1); int l1=strlen(s+1),l2=strlen(s1+1); n=l1+l2+1; s[l1+1]='#'; for(int i=1;i<=l2;i++) s[l1+1+i]=s1[i]; SA(); gethe(); //printf("s = %s ",s+1); //for(int i=1;i<=n;i++) printf("%d%c",sa[i],i==n?' ':' '); //for(int i=1;i<=n;i++) printf("%d%c",he[i],i==n?' ':' '); int top=0; LL res=0,ans=0; for(int i=1;i<=n;i++)//对于每一个i算出[1,i-1]对i点的贡献res, 这一次的res由上一次的res转移得到 { if(he[i]<k) top=res=0; else { num[i]=0; if(sa[i-1]<=l1) num[i]++,res+=he[i]-k+1; while(top>0&&he[sta[top]]>=he[i]) num[i]+=num[sta[top]], res-=num[sta[top]]*(he[sta[top]]-he[i]),top--;//加入了一个更小的,由于之前是直接加上的he[i]-k+1,根据lcp=min(...),对于i就减去之前多加的值 sta[++top]=i;//可以理解为把凸起来的山包给削平了 if(sa[i]>l1+1) ans+=res; } } for(int i=1;i<=n;i++)//对于每一个i算出[1,i-1]对i点的贡献res { if(he[i]<k) top=res=0; else { num[i]=0; if(sa[i-1]>l1+1) num[i]++,res+=he[i]-k+1; while(top>0&&he[sta[top]]>=he[i]) num[i]+=num[sta[top]], res-=num[sta[top]]*(he[sta[top]]-he[i]),top--; sta[++top]=i; if(sa[i]<=l1) ans+=res; } } printf("%lld ",ans); for(int i=1;i<=n;i++) x[i]=y[i]=0; } return 0; }
1 #include<cstdio> 2 #include<cstring> 3 #include<ctype.h> 4 #include<functional> 5 #include<algorithm> 6 #pragma GCC optimize(2) 7 using namespace std; 8 typedef long long LL; 9 typedef pair<int,int> pii; 10 typedef pair<LL,LL> pLL; 11 #define pb push_back 12 #define mk make_pair 13 #define fi first 14 #define se second 15 #define ls (i<<1) 16 #define rs (i<<1|1) 17 #define mem(a,b) memset(a,b,sizeof(a)) 18 const int N=1e6+5; 19 const int inf=0x3f3f3f3f; 20 const LL mod=1e9+7; 21 LL read() 22 { 23 LL x=0,f=1; 24 char ch=getchar(); 25 while(!isdigit(ch)){ if(ch=='-') f=-1; ch=getchar(); } 26 while(isdigit(ch)){ x=10*x+ch-'0'; ch=getchar(); } 27 return f*x; 28 } 29 pii a[N]; 30 int sa[N],rk[N],he[N],x[N],y[N],c[N],n,m,f[N],cs[N],ct[N]; 31 char s[N],t[N]; 32 void SA() 33 { 34 m=128; 35 for(int i=1;i<=m;i++) c[i]=0; 36 for(int i=1;i<=n;i++) c[x[i]=s[i]]++; 37 for(int i=1;i<=m;i++) c[i]+=c[i-1]; 38 for(int i=n;i;i--) sa[c[x[i]]--]=i; 39 for(int k=1;k<=n;k<<=1) 40 { 41 int tot=0; 42 for(int i=n-k+1;i<=n;i++) y[++tot]=i; 43 for(int i=1;i<=n;i++) if(sa[i]>k) y[++tot]=sa[i]-k;//把sa当做第二关键字的排名(第一关键字和第二关键字等长) 44 for(int i=1;i<=m;i++) c[i]=0; 45 for(int i=1;i<=n;i++) c[x[i]]++; 46 for(int i=1;i<=m;i++) c[i]+=c[i-1]; 47 for(int i=n;i;i--) sa[c[x[y[i]]]--]=y[i]; 48 swap(x,y); 49 x[sa[1]]=tot=1; 50 for(int i=2;i<=n;i++) 51 x[sa[i]]=y[sa[i]]==y[sa[i-1]]&&y[sa[i]+k]==y[sa[i-1]+k]?tot:++tot; 52 if(tot==n) break; 53 m=tot; 54 } 55 56 } 57 void gethe() 58 { 59 he[1]=0; 60 for(int i=1;i<=n;i++) rk[sa[i]]=i; 61 for(int i=1,k=0;i<=n;i++) 62 { 63 if(rk[i]==1) continue; 64 int j=sa[rk[i]-1]; 65 if(k) k--; 66 while(i+k<=n&&j+k<=n&&s[i+k]==s[j+k]) k++; 67 he[rk[i]]=k; 68 } 69 } 70 int getf(int x) 71 { 72 return x==f[x]?f[x]:f[x]=getf(f[x]); 73 } 74 int main() 75 { 76 int k; 77 while(scanf("%d",&k)!=EOF&&k) 78 { 79 scanf("%s%s",s+1,t+1); 80 int l1=strlen(s+1),l2=strlen(t+1); 81 s[l1+1]='#'; 82 n=l1+l2+1; 83 for(int i=1;i<=l2;i++) s[i+l1+1]=t[i]; 84 SA(); gethe(); 85 for(int i=1;i<=n;i++) 86 { 87 f[i]=i; 88 cs[i]=(i<=l1); 89 ct[i]=(i>l1+1); 90 } 91 for(int i=1;i<=n;i++) a[i]=mk(he[i],i); 92 sort(a+1,a+n+1); 93 LL ans=0; 94 // for(int i=1;i<=n;i++) printf("%d%c",sa[i],i==n?' ':' '); 95 for(int i=n;i;i--) 96 { 97 //printf("%d %d ",a[i].fi,a[i].se); 98 if(a[i].fi<k) break; 99 int id=a[i].se; 100 //if(rk[id]==1) continue; 101 int fx=getf(sa[id]),fy=getf(sa[id-1]); 102 if(fx==fy) continue; 103 f[fy]=fx; 104 ans+=1LL*(cs[fy]*ct[fx]+cs[fx]*ct[fy])*(a[i].fi-k+1); 105 cs[fx]+=cs[fy]; 106 ct[fx]+=ct[fy]; 107 } 108 printf("%lld ",ans); 109 for(int i=1;i<=n;i++) x[i]=y[i]=0; 110 } 111 return 0; 112 }