【技巧:字符串同构】Avendesora
判断字符串“同构”的技巧
题目大意
给定A,B两个序列,要求B在A中出现的次数以及位置。定义字符变换:把所有相同的字符变为另一种字符;两个字符串相等:当且仅当一个字符串可以在若干次字符变换之后变为另一个字符串。
题目分析
如果没有这个“字符变换”的条件,显然就是裸的KMP题。
不过有一种判断同构的方法:将数列权值转为它离之前最近相同元素的距离。
用$lst[i]$表示$a[i]$元素前的最近元素位置,就是$a[i]=i-lst[i]$。
那么再接下去做kmp就好了。
1 #include<bits/stdc++.h> 2 const int maxn = 1000035; 3 4 int T,c,n,m; 5 int a[maxn],b[maxn],lsta[maxn],lstb[maxn],pre[maxn]; 6 int fail[maxn]; 7 int ans[maxn]; 8 9 int read() 10 { 11 char ch = getchar(); 12 int num = 0; 13 bool fl = 0; 14 for (; !isdigit(ch); ch = getchar()) 15 if (ch=='-') fl = 1; 16 for (; isdigit(ch); ch = getchar()) 17 num = (num<<1)+(num<<3)+ch-48; 18 if (fl) num = -num; 19 return num; 20 } 21 int main() 22 { 23 freopen("ex_t2.in","r",stdin); 24 T = read(), c = read(); 25 while (T--) 26 { 27 memset(fail, 0, sizeof fail); 28 memset(pre, 0, sizeof pre); 29 ans[0] = 0, n = read(), m = read(); 30 for (int i=1; i<=n; i++) 31 { 32 a[i] = read(); 33 lsta[i] = pre[a[i]]; 34 pre[a[i]] = i; 35 } 36 memset(pre, 0, sizeof pre); 37 for (int i=1; i<=m; i++) 38 { 39 b[i] = read(); 40 lstb[i] = pre[b[i]]; 41 pre[b[i]] = i; 42 } 43 for (int i=1, j=0; i<m; i++) 44 { 45 while (j&&lstb[j+1]!=std::max(lstb[i+1]-i+j, 0)) j = fail[j]; 46 if (lstb[j+1]==std::max(lstb[i+1]-i+j, 0)) j++; 47 fail[i+1] = j; 48 } 49 for (int i=0, j=0; i<n; i++) 50 { 51 while (j&&lstb[j+1]!=std::max(lsta[i+1]-i+j, 0)) j = fail[j]; 52 if (lstb[j+1]==std::max(lsta[i+1]-i+j, 0)) j++; 53 if (j==m){ 54 ans[++ans[0]] = i-m+2; 55 j = fail[j]; 56 } 57 } 58 printf("%d\n",ans[0]); 59 for (int i=1; i<=ans[0]; i++) printf("%d ",ans[i]); 60 puts(""); 61 } 62 return 0; 63 }
END