【ybtoj】【kmp】字符串匹配

题意

image
image

题解

可以考虑转化到 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;
}
posted @ 2021-09-17 17:50  conprour  阅读(61)  评论(1编辑  收藏  举报