题目描述
黑暗之主的蜈蚣几乎可以毁灭一切,因此小正方形陷入了苦战……
小正方形现在需要减弱黑暗之主的攻击。
一个黑暗之主的攻击可以用一个仅有小写字母的字符串表示。
现在黑暗之主向小正方形发动了若干攻击,对于两个攻击,小正方形能选出它们最长的公共子串,并把这一段消除。
现在小正方形想要知道,对于任意两个黑暗之主的攻击,它们的最长公共子串长度是多少,你能帮帮它吗?
题解
- 先把所有的字符串拼起来,每两个之间用个奇奇怪怪的字符隔出,然后就跑SA
- 题目要求是要求最长公共字串,我们要找的是最长的,又因为ans是一段height的min,所以肯定找最近的
- 于是,pre[i]不再维护位置了,维护最近一次出现的位置到当前的min(height)就行了
代码
1 #include <cstdio> 2 #include <cstring> 3 #include <iostream> 4 #define N 2000010 5 #define M 60 6 using namespace std; 7 int n,l,m=600,t[N],x[N],c[N],sa[N],bel[N],height[N],rank[N],pre[M],len[M],ans[M][M]; 8 char s[N]; 9 void get_SA() 10 { 11 l=len[n]+1; 12 for (int i=1;i<=l;i++) c[x[i]=s[i]]++; 13 for (int i=2;i<=m;i++) c[i]+=c[i-1]; 14 for (int i=l;i>=1;i--) sa[c[x[i]]--]=i; 15 for (int k=1;k<=l;k<<=1) 16 { 17 int p=0; 18 for (int i=l-k+1;i<=l;i++) t[++p]=i; 19 for (int i=1;i<=l;i++) if (sa[i]>k) t[++p]=sa[i]-k; 20 for (int i=1;i<=m;i++) c[i]=0; 21 for (int i=1;i<=l;i++) c[x[i]]++; 22 for (int i=2;i<=m;i++) c[i]+=c[i-1]; 23 for (int i=l;i>=1;i--) sa[c[x[t[i]]]--]=t[i],t[i]=0; 24 swap(x,t); 25 p=1,x[sa[1]]=1; 26 for (int i=2;i<=l;i++) x[sa[i]]=(t[sa[i-1]]==t[sa[i]]&&t[sa[i-1]+k]==t[sa[i]+k])?p:++p; 27 if (p==l) break; 28 m=p; 29 } 30 int k=0; 31 for (int i=1;i<=l;i++) rank[sa[i]]=i; 32 for (int i=1;i<=l;i++) 33 { 34 if (rank[i]==1) continue; 35 if (k) k--; 36 int j=sa[rank[i]-1]; 37 while (j+k<=l&&i+k<=l&&s[i+k]==s[j+k]) k++; 38 height[rank[i]]=k; 39 } 40 } 41 int main() 42 { 43 scanf("%d",&n),len[0]=-1; 44 for (int i=1;i<=n;i++) 45 { 46 char ch; 47 while (!isalpha(ch=getchar())); 48 for (len[i]=len[i-1]+1,s[++len[i]]=ch,bel[len[i]]=i;isalpha(ch=getchar());s[++len[i]]=ch,bel[len[i]]=i); 49 s[len[i]+1]=300+i; 50 } 51 get_SA(); 52 for (int i=2;i<=l;i++) 53 { 54 for (int j=1;j<=n;j++) pre[j]=min(pre[j],height[i]); 55 int b=bel[sa[i]]; pre[bel[sa[i-1]]]=height[i]; 56 if (!b) continue; 57 for (int j=1;j<=n;j++) ans[j][b]=ans[b][j]=max(ans[b][j],pre[j]); 58 } 59 for (int i=1;i<=n;i++) { for (int j=1;j<=n;j++) if (i!=j) printf("%d ",ans[i][j]); printf(" "); } 60 }