jzoj 3189. 【GDOI2013模拟8】解密

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\n",i-cnt+1);
	}
	puts("0");
	return 0;
}
posted @ 2019-04-17 22:05  jz929  阅读(149)  评论(0编辑  收藏  举报