3208. 【JSOI2013】编程作业

Description&Data Constraint

Will相信,很多同学都有过这样的经历:大牛已经写好了编程作业,而作为菜鸟的自己不会写怎么办呢?拿大牛的代码抄一下嘛!但是提交一模一样的作业是不是不太好?于是就改一改变量名什么的……但是其实这样的代码抄袭行为是可以被检测出来的。

考虑到如下的两段代码,很容易发现他们其实是一样的。

那么最开始给出的两段雷同代码就可以分别写成 AiBjCiDECjDiFGC 以及 AaBiCaDECiDaFGC。或者简单的说,我们认为这两段代码是一样的。

现在请写一个程序,处理若干这样的代码雷同检测问题:给一个完整代码以及一个较短的代码片段,请求出,这个代码片段在完整代码中一共出现了多少次(代码片段出现的位置可以重叠)。

为了简单起见,我们认为程序中只会至多出现 (a)~(z) 这26个变量,同时也至多只有 (A) ~ (Z) 这26个非变量符号。

对于100%的数据满足 (Qle3)(|T|le100000)(|S|le1000000)

Solution

这题就是一个很明显的KMP,但是关键在于对小写字母的处理。

其实可以发现,小写字母是什么并不重要,重要的是他们之间的位置。而大写字母要保持一致。

因此可以对于每个小写字母,那个位置变成他与上一个同样的字母之间的距离,如果这个距离是一样的,那么就是可以匹配的。

但是这并不是所有。

举个例子,完整代码: (aababa),代码片段:(cdcd)

根据上述的转换可以变成 (010222)(0022)。发现如果仅仅在这上面跑 KMP 是会遗漏答案的。

因为对于位置 4 的 (a) 来说,在完整的代码中它距离上一个为 2,但是如果在完整片段中从第 3 个开始匹配,第 4 个 (a) 就变成了第一个,就可以与代码片段匹配。

所以对于当前匹配的区间中,如果一个字母是第一次出现,那么它可以跟任何一个数字匹配,前提是在当前区间中它是第一个。

Code

#include<cstdio>
#include<cstring>
#define N 1000005
using namespace std;
int T,n,m,ans,match[N<<1],t[30],a[N],b[N],s[N<<1];
char ch;
void init1()
{
	memset(t,-1,sizeof(t));
	while (!((ch>='a'&&ch<='z')||(ch>='A'&&ch<='Z'))) ch=getchar();
	while (((ch>='a'&&ch<='z')||(ch>='A'&&ch<='Z')))
	{
		if (ch>='a'&&ch<='z')
		{
			t[ch-'a'+1]==-1?a[n]=0:a[n]=n-t[ch-'a'+1]; 
			t[ch-'a'+1]=n;
			++n;
		}
		else a[n++]=-(ch-'A'+1);
		ch=getchar();
	}
}
void init2()
{
	memset(t,-1,sizeof(t));
	while (!((ch>='a'&&ch<='z')||(ch>='A'&&ch<='Z'))) ch=getchar();
	while (((ch>='a'&&ch<='z')||(ch>='A'&&ch<='Z')))
	{
		if (ch>='a'&&ch<='z')
		{
			t[ch-'a'+1]==-1?b[m]=0:b[m]=m-t[ch-'a'+1]; 
			t[ch-'a'+1]=m;
			++m;
		}
		else b[m++]=-(ch-'A'+1);
		ch=getchar();
	}
}
bool check(int x,int y,int len)
{
	if (x<0||y<0) return x==y;
	return x==y||(y==0&&x>len);
}
int main()
{
	scanf("%d",&T);
	while (T--)
	{
		n=m=ans=0;
		memset(match,0,sizeof(match));
		memset(a,0,sizeof(a));
		memset(b,0,sizeof(b));
		init1();init2();
		for (int i=0;i<m;++i)
			s[i]=b[i];
		s[m]=100;
		for (int i=0;i<n;++i)
			s[m+i+1]=a[i];
		for (int i=1;i<=n+m;++i)
		{
			int j=match[i-1];
			while (j>0&&!check(s[i],s[j],j)) j=match[j-1];
			if (check(s[i],s[j],j)) ++j;
			match[i]=j;
		}
		for (int i=m+1;i<=n+m;++i)
			if (match[i]==m) ++ans;
		printf("%d
",ans);
	}
	return 0;
} 
原文地址:https://www.cnblogs.com/Livingston/p/15367336.html