應用範圍
對於一個匹配串(S)和單個或多個母串(B)的問題
流程
- 先對匹配串造fail數組和AC自動機中的fail類似,即以失敗的節點爲右端點的
- 複雜度約爲
O(n+m)
void pre(){
fail[1]=0;fail[2]=1;
for(int i=1;i<=m;i++){
int j=fail[i];
while(j&&S[i]!=S[j])j=fali[j];
//此時滿足S[i-j+1,i]和S[1,j]相等
fail[i+1]=j+1;
}
}
- 和母串進行匹配
void match(){
for(int i=1,j=1;i<=n;i++){
while(j&&B[i]!=S[j])j=fali[j];
//此時滿足B[i-j+1,i]和S[1,j]相等
j++;
if(j=m+1){
//get the ans
j=fail[j];
}
}
}
一些變形
- 對於一些問題相等的判斷肯定不會只是數值的相等,可能有一些奇葩的定義
例1
- 我們定義兩個字符串相等:是他們的其中的每個字符在該段中的相對大小一一對應,就像下面的例子(串中字符大小屬於[1,26])
5 6 2 10 10 7 3 2 9
1 4 4 3 2 1
- 此時我們只需改變判斷相等的方法即可,但是一定不能忽略了匹配的流程:一個一個的判斷(即當前有一對相同的串,我們只需判斷它們的尾端加入後新的串是否相等)
- 我們發現字符大小很小,於是我們可以記錄一個關於[1,26]前綴數組即可
bool cmp(int A[M][30],int l1,int r1,int a,int B[M][30],int l2,int r2,int b){
for(int i=1;i<=t;i++){
ta[i]=A[r1][i]-A[l1-1][i]+ta[i-1];
tb[i]=B[r2][i]-B[l2-1][i]+tb[i-1];
}
return ta[a-1]==tb[b-1]&&ta[a]==tb[b];//加入前後都相等
}
int main(){
/**讀入,構造前綴和**/
fail[2]=1;
for(int i=2,j;i<=m;i++){
j=fail[i];
while(j&&!cmp(Ss,1,j-1,S[j],Ss,i-j+1,i-1,S[i]))j=fail[j];
fail[i+1]=j+1;
}
for(int i=1,j=1;i<=n;i++){
while(j&&!cmp(Bs,i-j+1,i-1,B[i],Ss,1,j-1,S[j]))j=fail[j];
j++;
if(j==m+1)ans[++ans[0]]=i-m+1,j=fail[j];
}
}
例2
- 對於兩端串,如果它們的“樣子一樣”,我們認爲它們相等
a b c x c z z a b c
prvi dr prvi tr tr x
-如圖
- 方法很簡單,我們只需記錄每個位置的字符上一次出現的下標,即可判斷一個新的點(段尾後一個)在該段中的位置,用它判斷即可
bool cmp1(int l1,int r1,int l2,int r2){
int ra=nxts[r1];
int rb=nxts[r2];
if(ra<l1&&rb<l2)return 1;
if(ra==rb)return 1;
return 0;
}
bool cmp2(int l1,int r1,int l2,int r2){
int ra=nxts[r1];
int rb=nxtb[r2];
if(ra<l1&&rb<l2)return 1;
if(ra-l1==rb-l2)return 1;
return 0;
}
int main(){
/**讀入&映射&造nxt**/
fail[2]=1;
for(int i=2,j;i<=m;i++){
j=fail[i];
while(j&&!cmp1(1,j,i-j+1,i))j=fail[j];
fail[i+1]=j+1;
}
for(int i=1,j=1;i<=n;i++){
while(j&&!cmp2(1,j,i-j+1,i))j=fail[j];
j++;
if(j==m+1){
printf("%d",i-m+1);
return 0;
}
}
}