Description
Will相信,很多同学都有过这样的经历:大牛已经写好了编程作业,而作为菜鸟的自己不会写怎么办呢?拿大牛的代码抄一下嘛!但是提交一模一样的作业是不是不太好?于是就改一改变量名什么的……但是其实这样的代码抄袭行为是可以被检测出来的。
考虑到如下的两段代码,很容易发现他们其实是一样的。
那么最开始给出的两段雷同代码就可以分别写成AiBjCiDECjDiFGC以及AaBiCaDECiDaFGC。或者简单的说,我们认为这两段代码是一样的。
现在请写一个程序,处理若干这样的代码雷同检测问题:给一个完整代码以及一个较短的代码片段,请求出,这个代码片段在完整代码中一共出现了多少次(代码片段出现的位置可以重叠)。
为了简单起见,我们认为程序中只会至多出现az这26个变量,同时也至多只有AZ这26个非变量符号。
Input
从文件homework.in中读入数据。
第一行包含一个整数Q表示此数据中一共包含Q个询问。
接下来2Q行,每两行为一个询问。
每个询问中的第一行包含一个字符串S,表示完整代码,第二行包含一个字符串T,表示需要检测出现次数的代码片段。
Output
输出到文件homework.out中。
一共输出Q行,每行一个整数,表示对应代码片段的出现次数。
Sample Input
3
AiBjCiDECjDiFGC
AaBiCaDECiDaFGC
cDEcDEbDE
aDEbDE
ccddef
aab
Sample Output
1
1
2
Data Constraint
对于30%的数据满足|T|<=10,|S|<=10000 ;
对于20%的数据满足任意S和T中仅含有大写字母;
对于100%的数据满足Q<=3,|T|<=100000,|S|<=1000000。
Hint
前两个样例均为题目中所举例的代码段。第三个样例中,在完整代码S中与代码片段T一样的片段为:ccd和dde。
分析:
两个字符串相同,当且仅当大写字母相同,小写字符通过替换也相同。
我们记录每一个小写字母在
T
T
T串中的一个位置。在进行匹配时,就把这个位置的小写字母直接替换成
S
S
S中对应位置的那个小写字母。
注意:如果对应的是大写字母,或者两个不同的小写字母对应同一个小写字母都不合法。
替换后的
T
T
T串的hash值与原hash值的差就可以推出来了。
举个例子,设
h
a
s
h
(
a
a
b
)
=
x
hash(aab)=x
hash(aab)=x,那么
h
a
s
h
(
c
c
b
)
=
11
0
(
52
)
∗
(
c
−
a
)
+
x
hash(ccb)=110_{(52)}*(c-a)+x
hash(ccb)=110(52)∗(c−a)+x
因为有大小写,所以在52进制下跑,然后判断hash值是否相同即可。
代码:
#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#define LL long long
const int maxn=1e6+7;
const LL mod[2]={1e9+9,998244353};
using namespace std;
int T,n,m,ans;
LL h[maxn][2],hash[2],bit[maxn][2],num[30][2];
int a[30],vis[30];
char s[maxn],t[maxn];
int getnum(char ch)
{
if (('A'<=ch) && (ch<='Z')) return ch-'A';
return ch-'a'+26;
}
void prework()
{
bit[0][0]=bit[0][1]=1;
for (int i=1;i<=n;i++)
{
for (int j=0;j<2;j++)
{
h[i][j]=(h[i-1][j]*52+getnum(s[i]))%mod[j];
bit[i][j]=(bit[i-1][j]*52)%mod[j];
}
}
for (int i=0;i<26;i++)
{
for (int j=0;j<2;j++) num[i][j]=0;
a[i]=0;
}
hash[0]=hash[1]=0;
for (int i=1;i<=m;i++)
{
for (int j=0;j<2;j++) hash[j]=(hash[j]*52+getnum(t[i]))%mod[j];
for (int k=0;k<26;k++)
{
for (int j=0;j<2;j++) num[k][j]=(num[k][j]*52)%mod[j];
}
if (('a'<=t[i]) && (t[i]<='z'))
{
a[t[i]-'a']=i;
for (int j=0;j<2;j++) num[t[i]-'a'][j]=(num[t[i]-'a'][j]+1)%mod[j];
}
}
}
LL gethash(int x,int len,int op)
{
int y=x+len-1;
return (h[y][op]+mod[op]-h[x-1][op]*bit[len][op]%mod[op])%mod[op];
}
int main()
{
scanf("%d",&T);
while (T--)
{
scanf("%s",s+1);
scanf("%s",t+1);
n=strlen(s+1);
m=strlen(t+1);
prework();
ans=0;
for (int i=1;i<=n-m+1;i++)
{
int flag=1;
for (int j=0;j<2;j++)
{
LL x=(gethash(i,m,j)+mod[j]-hash[j])%mod[j];
LL y=0;
for (int k=0;k<26;k++) vis[k]=0;
for (int k=0;k<26;k++)
{
if (!a[k]) continue;
if ((('A'<=s[i+a[k]-1]) && (s[i+a[k]-1]<='Z')) || (vis[s[i+a[k]-1]-'a']))
{
flag=0;
break;
}
vis[s[i+a[k]-1]-'a']=1;
LL delta=(s[i+a[k]-1]+mod[j]-t[a[k]])%mod[j];
y=(y+delta*num[k][j]%mod[j])%mod[j];
}
if (x!=y)
{
flag=0;
break;
}
}
ans+=flag;
}
printf("%d\n",ans);
}
}