[KMP]一本通(http://ybt.ssoier.cn:8088) 1698:字符串匹配

Posted tbiblog

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[KMP]一本通(http://ybt.ssoier.cn:8088) 1698:字符串匹配相关的知识,希望对你有一定的参考价值。

字符串匹配

【题目描述】

对于一个字符集大小为C的字符串pp,可以将任意两个字符在p中的位置进行互换,例如p=12321,交换1212得到21312,交换14得到42324,交换可以进行任意次。若交换后p变成了字符串q,则成qp是匹配的。

给定两个字符集大小为C的字符串st,求出s中有多少个连续子串与t匹配。

【输入】

第一行两个整数TC,分别表示数据组数和字符集大小,字符用1C的整数来表示。

对于每组数据:第一行两个整数nm,分别表示st的长度。

第二行n个正整数表示s

第三行m个正整数表示t

【输出】

对于每组数据,输出包括两行:

第一行一个正整数k,表示s中有k个连续子串与t匹配。

第二行从小到大输出k个数,表示s中与t匹配的连续子串的首位下标(下标从1开始)。

【输入样例】

3 3

6 3

1 2 1 2 3 2

3 1 3

6 3

1 2 1 2 1 2

3 1 3

6 3

1 1 2 1 2 1

3 1 3

【输出样例】

3

1 2 4

4

1 2 3 4

3

2 3 4

【数据规模及约定】

对于10%的数据,满足n,m,C1000n,m,C1000

对于另外20%的数据,满足n,m105C40n,m105C40

对于另外30%的数据,满足n,m,C105n,m,C105

对于100%的数据,满足1n,m,C106T=31n,m,C106T=3

【分析】

这其实就是一道KMP的题

题目的难点在于如何交换字符

我们可以开一个数组l[1~c]

l[x]表示上一个x出现的位置

a[i]表示字符s[i]离上一个相同字符出现的距离

b[i]表示字符t[i]离上一个相同字符出现的距离

然后就是KMP

难点是如何判断s[i](t[i])的上一个相同字符是否在模式串之外

这其实很简单

直接判断上一个x出现的距离是否大于j不就行了

【AC代码

#include<cstdio>
#include<cstring>
#include<algorithm>
#define N (1000000+2)
#define C (1000000+2)
using namespace std;
int p[N];
int a[N],b[N];
int l[C];
int ans[N];
template<typename T>inline void read(T& x)
	char temp=getchar();bool u=0;
	for(x=0;temp<‘0‘||temp>‘9‘;u=temp==‘-‘,temp=getchar());
	for(;temp>=‘0‘&&temp<=‘9‘;x=x*10+temp-‘0‘,temp=getchar());
	if(u)x=-x;
	return ;
//快读
inline int jdg(int x,int l)return x<l?x:0;//判断s[i](t[i])是否在匹配范围内
                              //是就返回a[i](b[i]),否就返回0
inline bool eq(int x,int y,int l)return jdg(x,l)==jdg(y,l);//判断是否匹配
void work()
	register int i,j,x;
	register int n,m;
	read(n);
	read(m);
	memset(a,0,sizeof a);
	memset(b,0,sizeof b);//有多组数据,一定要初始化
	memset(p,0,sizeof p);//否则就会成为dengzhaoxing之二
	memset(l,0,sizeof l);
	for(i=1;i<=n;i++)
		read(x);
		a[i]=i-l[x];
		l[x]=i;
	
	memset(l,0,sizeof l);//输入s和t之前都要初始化l
	for(i=1;i<=m;i++)
		read(x);
		b[i]=i-l[x];//将b[i]赋值为x与上一x之间的距离
		l[x]=i;
	
	for(i=1,j=0;i<m;i++)
		while(j&&!eq(b[i+1],b[j+1],j+1))
			j=p[j];
		if(eq(b[i+1],b[j+1],j+1))
			j++;
		p[i+1]=j;
	//初始化p数组
	for(x=i=j=0;i<n;i++)
		while(j&&!eq(a[i+1],b[j+1],j+1))
			j=p[j];
		if(eq(a[i+1],b[j+1],j+1))
			j++;
		if(j==m)
			ans[++x]=i+2-m;
			j=p[j];
		
	//匹配
	printf("%d\n",x);
	for(i=1;i<=x;i++)
		printf("%d ",ans[i]);//输出答案
	putchar(‘\n‘);
	return ;

int main()
	register int t,c,i;
	read(t);
	read(c);
	for(i=1;i<=t;i++)
		work();
	return 0;

  

  

以上是关于[KMP]一本通(http://ybt.ssoier.cn:8088) 1698:字符串匹配的主要内容,如果未能解决你的问题,请参考以下文章

KMP算法

Censoring(栈+KMP)

Power Strings (KMP求最小循环节)

信息学奥赛一本通要多少钱

信息学奥赛一本通为啥不通过

信息奥赛一本通 1060:均值