BZOJ4032:[HEOI2015]最短不公共子串(SAM)
Description
在虐各种最长公共子串、子序列的题虐的不耐烦了之后,你决定反其道而行之。
一个串的“子串”指的是它的连续的一段,例如bcd是abcdef的子串,但bde不是。
一个串的“子序列”指的是它的可以不连续的一段,例如bde是abcdef的子串,但bdd不是。
下面,给两个小写字母串A,B,请你计算:
(1) A的一个最短的子串,它不是B的子串
(2) A的一个最短的子串,它不是B的子序列
(3) A的一个最短的子序列,它不是B的子串
(4) A的一个最短的子序列,它不是B的子序列
Input
有两行,每行一个小写字母组成的字符串,分别代表A和B。
Output
输出4行,每行一个整数,表示以上4个问题的答案的长度。如果没有符合要求的答案,输出-1.
Sample Input
aabbcc
abcabc
abcabc
Sample Output
2
4
2
4
4
2
4
HINT
对于100%的数据,A和B的长度都不超过2000
Solution
强行四合一?
(一)枚举$A$串的左端点,然后从左端点开始往后在$B$串的$SAM$上面跑,一旦失配就更新答案然后$break$
(二)预处理数组$next[i][j]$表示从$i$后面的第一次出现字母$j$的位置。预处理出$nextA$和$nextB$,然后枚举$A$左端点往后贪心,如果失配就更新答案然后$break$
(三)设$len[i]$表示在$B$串的$SAM$的$i$点的时候最短的长度。然后用$A$串在$B$的$SAM$上面跑。如果失配就更新答案,否则就更新$len$。
(四)设$len[i]$表示在$B$串的$i$位置的时候最短的长度。然后用$A$串的每一个字母,借$next$数组倒序去更新$len$。如果失配就更新答案,否则就更新$len$。至于为什么要倒序,其实是和背包差不多的原理,并不难想。
Code
1 #include<iostream> 2 #include<cstring> 3 #include<cstdio> 4 #define N (4009) 5 using namespace std; 6 7 char s[N],t[N]; 8 int slen,tlen,nextA[N][28],nextB[N][28],last[28],len[N]; 9 10 struct SAM 11 { 12 int son[N][28],fa[N],step[N],wt[N],od[N]; 13 int p,q,np,nq,last,cnt; 14 SAM(){last=cnt=1;} 15 16 void Insert(int x) 17 { 18 p=last; np=last=++cnt; step[np]=step[p]+1; 19 while (p && !son[p][x]) son[p][x]=np, p=fa[p]; 20 if (!p) fa[np]=1; 21 else 22 { 23 q=son[p][x]; 24 if (step[q]==step[p+1]) fa[np]=q; 25 else 26 { 27 nq=++cnt; step[nq]=step[p]+1; 28 memcpy(son[nq],son[q],sizeof(son[q])); 29 fa[nq]=fa[q]; fa[np]=fa[q]=nq; 30 while (son[p][x]==q) son[p][x]=nq, p=fa[p]; 31 } 32 } 33 } 34 }SAM; 35 36 void CalcNext() 37 { 38 memset(last,-1,sizeof(last)); 39 for (int i=slen; i>=0; --i) 40 { 41 for (int j=0; j<26; ++j) nextA[i][j]=last[j]; 42 last[s[i]-'a']=i; 43 } 44 memset(last,-1,sizeof(last)); 45 for (int i=tlen; i>=0; --i) 46 { 47 for (int j=0; j<26; ++j) nextB[i][j]=last[j]; 48 last[t[i]-'a']=i; 49 } 50 } 51 52 void Sub1() 53 { 54 int ans=2e9; 55 for (int i=1; i<=slen; ++i) 56 { 57 int now=1; 58 for (int j=i; j<=slen; ++j) 59 { 60 if (!SAM.son[now][s[j]-'a']) {ans=min(ans,j-i+1); break;} 61 now=SAM.son[now][s[j]-'a']; 62 } 63 } 64 printf("%d\n",ans==2e9?-1:ans); 65 } 66 67 void Sub2() 68 { 69 int ans=2e9; 70 for (int i=1; i<=slen; ++i) 71 { 72 int now=0; 73 for (int j=i; j<=slen; ++j) 74 { 75 if (nextB[now][s[j]-'a']==-1) {ans=min(ans,j-i+1); break;} 76 now=nextB[now][s[j]-'a']; 77 } 78 } 79 printf("%d\n",ans==2e9?-1:ans); 80 } 81 82 void Sub3() 83 { 84 int ans=2e9; 85 memset(len,0x7f,sizeof(len)); 86 len[1]=1; 87 for (int i=1; i<=slen; ++i) 88 for (int j=1; j<=SAM.cnt; ++j) 89 { 90 int nxt=SAM.son[j][s[i]-'a']; 91 if (!nxt) ans=min(ans,len[j]); 92 else len[nxt]=min(len[nxt],len[j]+1); 93 } 94 printf("%d\n",ans==2e9?-1:ans); 95 } 96 97 void Sub4() 98 { 99 int ans=2e9; 100 memset(len,0x7f,sizeof(len)); 101 len[0]=0; 102 for (int i=1; i<=slen; ++i) 103 for (int j=tlen; j>=0; --j) 104 { 105 int nxt=nextB[j][s[i]-'a']; 106 if (nxt==-1) ans=min(ans,len[j]+1); 107 else len[nxt]=min(len[nxt],len[j]+1); 108 } 109 printf("%d\n",ans==2e9?-1:ans); 110 } 111 112 int main() 113 { 114 scanf("%s%s",s+1,t+1); 115 slen=strlen(s+1), tlen=strlen(t+1); 116 for (int i=1; i<=tlen; ++i) 117 SAM.Insert(t[i]-'a'); 118 CalcNext(); 119 Sub1(); Sub2(); Sub3(); Sub4(); 120 }