【ybtoj】【kmp】字符串匹配
题意
题解
可以考虑转化到 KMP 相关问题。
KMP 的常规匹配是看当前字符位是否相同,而本题可以改为当前字符和前一个相同字符的距离是否相同,因为这样的字符串经过反转之后一定能变成相同的。
当前字符到前一个相同字符的距离 \(pre_i\) 数组可以预处理。
注意:当与前一个相同字符的距离大于当前匹配到的位置时,是没有意义的,当作首次出现处理(赋值为 \(0\))。
代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int INF = 0x3f3f3f3f,N = 1e6+10;
inline ll read()
{
ll ret=0;char ch=' ',c=getchar();
while(!(c>='0'&&c<='9')) ch=c,c=getchar();
while(c>='0'&&c<='9') ret=(ret<<1)+(ret<<3)+c-'0',c=getchar();
return ch=='-'?-ret:ret;
}
inline int ask(int l,int r){return l<r?l:0;}
int T,c,n,m,s[N],t[N];
int lst[N],preb[N],prea[N],pos[N],nxt[N],ans;
inline void pretreat()
{
int j=0;
for(int i=1;i<m;i++)
{
while(j&&ask(preb[i+1],j+1)!=ask(preb[j+1],j+1)) j=nxt[j];
if(ask(preb[i+1],j+1)==ask(preb[j+1],j+1)) j++;
nxt[i+1]=j;
}
}
inline void kmp()
{
int j=0; ans=0;
for(int i=0;i<n;i++)
{
while(j&&ask(prea[i+1],j+1)!=ask(preb[j+1],j+1)) j=nxt[j];
if(ask(prea[i+1],j+1)==ask(preb[j+1],j+1)) j++;
if(j==m) pos[++ans]=i+1-m+1,j=nxt[j];
}
}
int main()
{
T=read(),c=read();
while(T--)
{
n=read(),m=read();
for(int i=1;i<=n;i++) s[i]=read();
for(int i=1;i<=m;i++) t[i]=read();
memset(lst,0,sizeof(lst));
for(int i=1;i<=n;i++)
{
prea[i]=ask(i-lst[s[i]],m);
lst[s[i]]=i;
}
memset(lst,0,sizeof(lst));
for(int i=1;i<=m;i++)
{
preb[i]=ask(i-lst[t[i]],m);
lst[t[i]]=i;
}
pretreat();
kmp();
printf("%d\n",ans);
for(int i=1;i<=ans;i++)
printf("%d ",pos[i]);
printf("\n");
}
return 0;
}