Description
Mirko要解一段加密文,但他只知道某一个句子是原文的一部分。你的任务是要在密文中找到第一个对应这个句子的地方。
文段是通过用某个单词(可能和原文一样的单词)替换原始文段每一个单词来加密的。如果某些单词在原文出现一次以上,就会使用相同的替换单词来替换。没有两个不同的单词使用相同的替换单词。
单词是通过空格隔开的小写字母序列。句子是连续单词的序列。
Input
第一行输入密文,这段文段不会有超过10^6个字符,每个单词之间只会有一个空格字符,行末处输入不是文段的一部分。
接下来一行输入出现在原文中的句子,这个句子就是我们要在密文中要找到的句子。句子不会超过10^6个字符,而且符合上面所描述的格式。
Output
输出一行,包括第一个对应原始文段句子的第一个单词的位置。
Sample Input
输入1:
a a a b c d a b c $
x y $
输入2:
xyz abc abc xyz $
abc abc $
输入3:
a b c x c z z a b c $
prvi dr prvi tr tr x $
Sample Output
输出1:
3
输出2:
2
输出3:
3
Data Constraint
N<=1000000
Solution
这题我™改了好久(一直WA80,后发现自己KMP打挂了(竟然还有80…数据是有多水))
我们发现,这题就是问母串中是否有与子串相形似的串,有则输出位置开头,否则return 0。
我们可以神奇地发现,如果串相匹配的话,那么当中的任意一个字母与前面与其相同的字母的间隔是一样的。(PS:如果母串中的前面字母的位置跳出了匹配位置的话也算匹配)
所以我们按照这个来hash一波,然后就可以用KMP来搞一搞了!!!
哈哈,这方法,真的是太牛逼了!!!
Code
#include<cstdio>
#include<cstring>
#define ctu while (c<'a' || c>'z') c=getchar()
#define gc getchar
#define N 1000010
#define mo 838379
#define MO 13578987531
#define ll long long
using namespace std;
int a[N],b[N],bf[N],tot=0,cnt=0;
int nx[N],pl[mo+10];
ll x,x1,h[mo+10]; char c;
int main()
{
freopen("secret.in","r",stdin);
// freopen("secret.out","w",stdout);
c=gc();ctu;
while (c!='$')
{
x=0;
while (c>='a' && c<='z')
x=(x*29+(c-'a'+1))%MO,c=gc();
x1=x%mo;
while (h[x1])
{
if (h[x1]==x)
{
a[++tot]=tot-pl[x1];
pl[x1]=tot;break;
}
x1=(x1+1)%mo;
}
if (!h[x1])
{
a[++tot]=0;
h[x1]=x,pl[x1]=tot;
}
c=gc();
}
memset(h,0,sizeof(h));
memset(pl,0,sizeof(pl));
c=gc();ctu;
while (c!='$')
{
x=0;
while (c>='a' && c<='z')
x=(x*29+(c-'a'+1))%MO,c=gc();
x1=x%mo;cnt++;
while (h[x1])
{
if (h[x1]==x)
{
b[cnt]=cnt-pl[x1];
pl[x1]=cnt;break;
}
x1=(x1+1)%mo;
}
if (!h[x1])
{
b[cnt]=0;
h[x1]=x,pl[x1]=cnt;
}
c=gc();
}
nx[1]=0;
for (int i=2,j=0;i<=cnt;i++)
{
while (j>0 && b[i]!=b[j+1]) j=nx[j];
if (b[i]==b[j+1]) j++;
nx[i]=j;
}
for (int i=1,j=0;i<=tot;i++)
{
while (j>0 && (a[i]!=b[j+1] && (b[j+1]>0 || j-a[i]>=0))) j=nx[j];
if (a[i]==b[j+1] || (b[j+1]==0 && j-a[i]<0)) j++;
if (j==cnt) return 0&printf("%d
",i-cnt+1);
}
puts("0");
return 0;
}