题目链接
题目的意思就是给你两个字符串数组,第二个字符串数组是密码本,每个字符串对应第一个数组的一个字符串,问你第二个字符串数组在第一个字符串数组第一次出现的位置
首先,怎样才是对应呢?
即每个字符串出现的频率和位置相同,像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;
}