• JZOJ 3189. 【GDOI2013模拟8】解密(KMP)


    3189. 【GDOI2013模拟8】解密

    题目

    Description

    Mirko要解一段加密文,但他只知道某一个句子是原文的一部分。你的任务是要在密文中找到第一个对应这个句子的地方。

    文段是通过用某个单词(可能和原文一样的单词)替换原始文段每一个单词来加密的。如果某些单词在原文出现一次以上,就会使用相同的替换单词来替换。没有两个不同的单词使用相同的替换单词。

    单词是通过空格隔开的小写字母序列。句子是连续单词的序列。

    Input

    第一行输入密文,这段文段不会有超过 1 0 6 10^6 106个字符,每个单词之间只会有一个空格字符,行末处输入"$","$"不是文段的一部分。

    接下来一行输入出现在原文中的句子,这个句子就是我们要在密文中要找到的句子。句子不会超过 1 0 6 10^6 106个字符,而且符合上面所描述的格式。

    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 N≤1000000 N1000000

    题解

    • 第一眼看上去,难道不是KMP吗???
    • 仔细看看,怎么实现呢,匹配串和原串的对应关系都不能确定,
    • 那么其实还有一种很巧妙的方法,
    • 把每个串转变为它离前面一个最近的和它相同的串的距离,
    • 这里用哈希维护,把每个单词按 27 27 27进制压成一个数,一定要模一个特别大的质数,
    • 可以用多个不同质数相乘再加 1 1 1,这样保证是个质数,一直乘到 1 0 12 10^{12} 1012左右。
    • 然后KMP放在这样转化后的串中比较。
    • 但有特殊情况,
    • 原串:A A B
    • 压缩:-1 1 -1
    • 匹配串:A B
    • 压缩:-1 -1
    • 如果就这么直接匹配是无解的,
    • 所以“-1”的匹配方式不同,
    • KMP中有一个匹配的指针 j j j,表示当前对应匹配串的第 j j j位,
    • 只要原串的当前位的值是“-1”,或者值大于等于 j j j,即说明前面 1 − ( j − 1 ) 1-(j-1) 1(j1)位不曾出现过重复这个单词,也就可以满足匹配条件。

    代码

    #include<cstdio>
    #include<cstring>
    using namespace std;
    #define LL long long
    #define md 200560490131
    #define mo 999983
    char s[1000010];
    LL e[1000010],h[2000010],sum[1000010];
    int a[1000010],b[1000010],next[1000010];
    int hash(LL t,int n)
    {
    	LL x=t%mo;
    	while(h[x])
    	{
    		if(h[x]==t) 
    		{
    			int p=sum[x];
    			sum[x]=n;
    			return p;
    		}
    		x=(x+1)%2000000;
    	}
    	h[x]=t;
    	sum[x]=n;
    	return 0;
    }
    int main()
    {
    	int i,j,n=0,m=0;
    	e[0]=1;
    	for(i=1;i<=1000000;i++) e[i]=e[i-1]*27%md;
    	while(1)
    	{
    		scanf("%s",s+1);
    		if(s[1]=='$') break;
    		n++;
    		LL t=0;
    		for(i=1;i<=strlen(s+1);i++) t=(t+(s[i]-'a'+1)*e[i-1]%md)%md;
    		int p=hash(t,n);
    		if(p==0) a[n]=0; else a[n]=n-p;
    	}
    	scanf("
    ");
    	memset(h,0,sizeof(h));
    	memset(sum,0,sizeof(sum));
    	while(1)
    	{
    		scanf("%s",s+1);
    		if(s[1]=='$') break;
    		m++;
    		LL t=0;
    		for(i=1;i<=strlen(s+1);i++) t=(t+(s[i]-'a'+1)*e[i-1]%md)%md;
    		int p=hash(t,m);
    		if(p==0) b[m]=0; else b[m]=m-p;
    	}
    	j=0;
    	for(i=2;i<=m;i++)
    	{
    		while(j>0&&b[j+1]!=b[i]) j=next[j];
    		if(b[j+1]==b[i]) j++;
    		next[i]=j;
    	}
    	j=0;
    	for(i=1;i<=n;i++)
    	{
    		while(j>0&&!(b[j+1]==a[i]||(b[j+1]==0&&a[i]>j))) j=next[j];
    		if(b[j+1]==a[i]||(b[j+1]==0&&a[i]>j)) j++;
    		if(j==m)
    		{
    			printf("%d
    ",i-m+1);
    			return 0;
    		}
    	}
    	return 0;
    }
    
    哈哈哈哈哈哈哈哈哈哈
  • 相关阅读:
    IBM MQ 学习
    spring中配置监听队列的MQ
    数据库优化(二)
    设计模式
    VBA学习笔记(2)--新建word文档并插入文字
    VBA代码分行
    excel保存时出现“请注意,您的文档的部分内容可能包含了文档检查器无法删除的个人信息”
    Excel VBA 操作 Word(入门篇)
    win10无法使用内置管理员账户打开应用
    五笔字根拆分规则_字根拆分方法
  • 原文地址:https://www.cnblogs.com/LZA119/p/13910078.html
Copyright © 2020-2023  润新知