题目链接:http://poj.org/problem?id=3461
题目描述:给一个子串再给一个主串,问子串在主串中出现了多少次。裸题一道,用来试验哈希和kmp模板
解题思路&AC代码:
(哎呀我去。。这题都要背下来了 = =
①–HASH
首先,我觉得哈希好写还好理解,先写了一个哈希的代码,所谓哈希就是给字符串生成一个哈希值,也就是算出一个数来,用这个数来代表这个字符串,显然对数的操作都是o(1)的,所以就达到了减小时间复杂度的作用。
有几个问题应该说明一下,因为一共就26个字母,所以取31进制比较好,然后这要是错了的话就改成1e6+7啥的试试,一般错不了,算哈希值的时候通常为了方便比较还会搞出一个hash数组来,计算这个数组的时候从后面往前算能使计算方便一点。需要再次强调的是哈希值只能“存储”这个字符串的信息,因为通常没几下就爆了ll,取模后的值就没什么数学意义了,千万不要拿哈希值去做“数学计算”!
不多说了。。贴代码
#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;
typedef unsigned long long ll;
const int base = 31;
const int maxn = 1000050;
char sub[maxn],str[maxn];
ll xp[maxn];
ll hash[maxn];
int main()
{
int T,i;
scanf("%d",&T);
xp[0]=1;
for(i=1;i<maxn;i++)
xp[i]=xp[i-1]*base;
while(T--)
{
memset(sub,0,sizeof(sub));
memset(str,0,sizeof(str));
scanf("%s",sub);
scanf("%s",str);
int L=strlen(sub);
int n=strlen(str);
ll sub_num=0;
for(i=L-1;i>=0;i--)
{
sub_num=sub_num*base+sub[i];
}
hash[n]=0;
for(i=n-1;i>=0;i--)
{
hash[i]=hash[i+1]*base+str[i];
}
int ans=0;
for(i=0;i<=n-L;i++) ///Caution!!! it is (i<=n-L) or (i<n-L+1)
{
if(sub_num==hash[i]-hash[i+L]*xp[L])
ans++;
}
printf("%d\n",ans);
}
return 0;
}
②–KMP
接下来是用KMP方法解决匹配问题,这个算法有点难以理解,我到现在也没完全搞明白,先拿模板这么用吧,且敲且理解。。。
子串与主串匹配的时候,一旦失配,朴素的算法上子串回溯到头,主串往后走一位,这是有浪费的
比如:
P: a b c a b e
S: a b c a b k a b c
e和k比失配了,我们没必要朴素算法那样回溯,而是把S串中c那个位置对准到k那个位置继续比较,这样做是因为ab先匹配上后面才有戏,所以KMP算法就搞出了一个next数组,next【i】存放的是一旦到 i 失配,我们该回溯到子串的什么位置。
如何计算出next数组是KMP算法的难点,事实上我们可以理解为next【i】代表了 i 以前的字符串的“自我重合度”。求next【i】的过程就是找 i 前面字符串的“前缀、后缀最长重合长度”。我们设 i , j 变量分别表示前后缀的最后字符位置,相等,那么欢天喜地都加一,失配,那么 i 回溯到next【i】,用这样一种递推似的思想来求出整个next数组。
以上理解说实话我心里也没底。。要多敲题再体会体会。。唉。。讲得太快了,每节例会的内容我觉得都够搞一个星期的。。。明天比赛了,不知道能不能进省赛,,加油吧~
#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;
const int maxn = 1000100;
char p[maxn],s[maxn];
int next[maxn];
int lenp,lens;
int KMP()
{
int i=0,j=0,ans=0;
while(i<lenp&&j<lens)
{
if(i==-1||p[i]==s[j])
{
i++;j++;
}
else
{
i=next[i];
}
if(i==lenp)
{
ans++;
i=next[i];
}
}
return ans;
}
void getnext()
{
next[0]=-1;
int i=0,j=-1;
while(i<lenp)
{
if(j==-1||p[i]==p[j])
{
i++;j++;
if(p[i]==p[j])next[i]=next[j];
else next[i]=j;
}
else
{
j=next[j];
}
}
}
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
scanf("%s",p);
scanf("%s",s);
lenp=strlen(p);
lens=strlen(s);
getnext();
/*
for(int i=0;i<lenp;i++)
cout<<next[i]<<" ";
cout<<endl;
*/
int ans=KMP();
printf("%d\n",ans);
}
return 0;
}