【哈希&KMP模板题】-POJ-3461-Oulipo

题目链接: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模板题】-POJ-3461-Oulipo》

《【哈希&KMP模板题】-POJ-3461-Oulipo》

②–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;
}

《【哈希&KMP模板题】-POJ-3461-Oulipo》

《【哈希&KMP模板题】-POJ-3461-Oulipo》

    原文作者:KMP算法
    原文地址: https://blog.csdn.net/cryssdut/article/details/24535825
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞