KMP习题

POJ - 3461 Oulipo

链接:http://poj.org/problem?id=3461

题意:给定字符串 s 和 t ,求 t 在 s 中出现的次数

思路

  • 第一种:假设模式串当前位置为 j ,当 j 为 1 时,就不用再走到 next[j] 了。因为 next[1]=0,我们需要将模式串指针从 j =1 开始匹配。
  • 第二种:构造 t +’#’+ s 这样的字符串,然后在 s 中计算最长公共前后缀长度为 n 的个数,maxL[i]==n,就是一个答案。因为这个新构造的字符串中的前缀就是我们所需要匹配的字符串。此时计算相同的后缀个数即可。
#include <cstdio>
#include <cstring>
#define ll long long
using namespace std;
const int maxn=1e6+5;
int n,m;
int nxt[maxn];
char s[maxn],t[maxn];
void getNext()
{
    nxt[1]=0;
    int i=1,j=0;
    while(i<=m)
    {
        if(j==0||t[i]==t[j]) nxt[++i]=++j;
        else j=nxt[j];
    }
}
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%s",t+1);
        scanf("%s",s+1);
        n=strlen(s+1);
        m=strlen(t+1);
        getNext();
        int ans=0;
        for(int i=1,j=1; i<=n; ++i)
        {
            while(j>1&&s[i]!=t[j]) j=nxt[j];
            if(s[i]==t[j]) j++;
            if(j==m+1)
            {
                ans++;
                j=nxt[j];
            }
        }
        printf("%d\n",ans);
    }
    return 0;
}
#include <cstdio>
#include <cstring>
#define ll long long
using namespace std;
const int maxn=2e6+5;

int n,m,len;
int maxL[maxn];
char s[maxn],t[maxn];

void getMaxL()
{
    maxL[1]=0;
    int j=0;
    for(int i=2; i<=len; ++i)
    {
        while(j!=0&&t[j+1]!=t[i]) j=maxL[j];
        if(t[j+1]==t[i]) j++;
        maxL[i]=j;
    }
}
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%s",t+1);
        scanf("%s",s+1);
        n=strlen(s+1);
        m=strlen(t+1);
        t[m+1]='#';
        for(int i=m+2; i<=n+m+1; ++i) t[i]=s[i-m-1];
        t[n+m+2]='\0';
        len=n+m+1;
        getMaxL();
        int ans=0;
        for(int i=m+2; i<=n+m+1; ++i)
            if(maxL[i]==m) ans++;
        printf("%d\n",ans);
    }
    return 0;
}

HDU - 3336 Count the string

链接:http://acm.hdu.edu.cn/showproblem.php?pid=3336

题意:求每个前缀在字符串中出现的次数之和。
思路:设 d p [ i ] dp[i] dp[i] 表示以 i i i 为右端点的所有前缀的出现次数,因此可以从 dp[maxL[i]] 转移过来,因为它只是在 dp[maxL[i]] 的基础上,多加了它本身。maxL[i] 表示 s[1~i] 中最长的相同的前后缀的长度。

#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=2e5+5,mod=10007;

int t,n;
char s[maxn];
int dp[maxn],maxL[maxn];

void getMaxL()
{
    maxL[1]=0;
    int j=0;
    for(int i=2; i<=n; ++i)
    {
        while(j!=0&&s[j+1]!=s[i]) j=maxL[j];
        if(s[j+1]==s[i]) j++;
        maxL[i]=j;
    }
}

int main()
{
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d",&n);
        scanf("%s",s+1);
        getMaxL();
        int ans=0;
        for(int i=0; i<=n; ++i) dp[i]=0;
        for(int i=1; i<=n; ++i)
        {
            dp[i]=(dp[maxL[i]]+1)%mod;
            ans=(ans+dp[i])%mod;
        }
        printf("%d\n",ans);
    }
    return 0;
}
  • 分别统计每个前缀出现的次数,然后相加即可。
  • 首先对每个前缀函数值,在maxL函数中出现的次数。也就是统计了把某些前缀作为最长后缀的次数。
  • 然后从后往前转移,对每个以 i 为右端点,但不是最长的前缀的前缀,再统计一次,它在作为后缀的次数。 a n s [ m a x L [ i ] ] + = a n s [ i ] ans[maxL[i]]+=ans[i] ans[maxL[i]]+=ans[i]
  • 最后每个前缀都自己出现一次,每个 ans[i] 加 1 。
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=2e5+5,mod=10007;

int t,n;
char s[maxn];
int ans[maxn],maxL[maxn];

void getMaxL()
{
    maxL[1]=0;
    int j=0;
    for(int i=2; i<=n; ++i)
    {
        while(j!=0&&s[j+1]!=s[i]) j=maxL[j];
        if(s[j+1]==s[i]) j++;
        maxL[i]=j;
    }
}

int main()
{
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d",&n);
        scanf("%s",s+1);
        getMaxL();
        for(int i=0; i<=n; ++i) ans[i]=0;
        for(int i=1; i<=n; ++i) ans[maxL[i]]++;
        for(int i=n; i>=1; --i) ans[maxL[i]]+=ans[i];
        for(int i=1; i<=n; ++i) ans[i]++;
        int res=0;
        for(int i=1; i<=n; ++i) res=(res+ans[i])%mod;
        printf("%d\n",res);
    }
    return 0;
}
/*
10
21
abcabxyzabcabzyxabcab
21
abcabxxxabcabxxxabcab
7
abababa
*/
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值