P8085 [COCI2011-2012#4] KRIPTOGRAM(kmp)

题目链接
题目的意思就是给你两个字符串数组,第二个字符串数组是密码本,每个字符串对应第一个数组的一个字符串,问你第二个字符串数组在第一个字符串数组第一次出现的位置

首先,怎样才是对应呢?
即每个字符串出现的频率和位置相同,像aabbc和bbccd是对应的

那么对应的条件是什么?
不难想到找到每个字符在这个字符串前一次出现的位置即可
如 aabbc 对应的数组 0 1 0 1 0
bbccd 对应的数组 0 1 0 1 0

重点来了
我们通过上面这样的方式,就将题意变成了最基础的kmp匹配了,
但是在实现过程中也有两个问题

一:

如 caabbc 和 bbccd
按照前面的写法他们对应的数组分别是
0 0 1 0 1 5
0 1 0 1 0
这样是不对的,所以我们要加一个判断:
如果当前的距离大于等于第二个数组的长度,就置为0

二:

如 aaabc和ab
他们对应的数组是
0 1 1 0 0
0 0
我们得到的答案会是4(正确答案是3),即会判断到bc和ab相匹配,而不是ab和ab匹配
这是因为前面字符有出现而且出现的距离小于第二个数组长度,我们的判断会跳过这个字符,所以我们在写kmp的时候要改正条件:
如果当前这个字符串距离上一次出现的位置>=我们匹配到的第二个字符串的位置,就把他的值视为0

代码如下:

#include<bits/stdc++.h>
const int maxn=1e6+100;
const int inf=2005020600;
#define ll long long
using namespace std;
int kmp[maxn],a[maxn],b[maxn],kmp1[maxn];
map<string,int>s1;
int main()
{
	string x;
	int n=0,m=0;
	while(1)
	{
		n++;
		cin>>x;
		if(x[0]=='$')break;
		if(s1[x]!=0)a[n]=n-s1[x];
		else a[n]=0;
		s1[x]=n;
	}
	n--;
	s1.clear();
	while(1)
	{
		m++;
		cin>>x;
		if(x[0]=='$')break;
		if(s1[x]!=0)b[m]=m-s1[x];
		else b[m]=0;
		s1[x]=m;
	}
	m--;
	int j=0;
	for(int i=2;i<=n;i++)
	{
		if(a[i]==0||a[i]>=m)kmp[i]=0;
		else kmp[i]=a[i];
	}
	j=0;
	kmp1[1]=1;
	for(int i=2;i<=m;i++)
	{
		while(j>1&&b[j+1]!=b[i])j=kmp1[j];
		if(b[j+1]==b[i])j++;
		kmp1[i]=j;
	}
	j=1;
	for(int i=2;i<=n;i++)
	{
		
		while(j>1&&b[j+1]!=kmp[i])
		{
			j=kmp1[j];
		}
		if(kmp[i]>=j+1)
		{
			if(b[j+1]==0)j++;
		}else
		{
			if(b[j+1]==kmp[i])j++;
		}		
		if(j==m)
		{
			printf("%d\n",i-j+1);
			return 0;
		}
	}
    return 0;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值