HDU 3336 (KMP上的dp问题,ac自动机我来了)

Count the string

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 4845    Accepted Submission(s): 2295


Problem Description
It is well known that AekdyCoin is good at string problems as well as number theory problems. When given a string s, we can write down all the non-empty prefixes of this string. For example:
s: "abab"
The prefixes are: "a", "ab", "aba", "abab"
For each prefix, we can count the times it matches in s. So we can see that prefix "a" matches twice, "ab" matches twice too, "aba" matches once, and "abab" matches once. Now you are asked to calculate the sum of the match times for all the prefixes. For "abab", it is 2 + 2 + 1 + 1 = 6.
The answer may be very large, so output the answer mod 10007.
 

Input
The first line is a single integer T, indicating the number of test cases.
For each case, the first line is an integer n (1 <= n <= 200000), which is the length of string s. A line follows giving the string s. The characters in the strings are all lower-case letters.
 

Output
For each case, output only one number: the sum of the match times for all the prefixes of s mod 10007.
 

Sample Input
  
  
1 4 abab
 

Sample Output
  
  
6
 

Author
foreverlin@HNU
 

Source
 


这道题由于n可以到200000,用任何O(n^2)的办法都是不合理的,可以用O(n)办法处理
首先,必须先透彻理解kmp的用法,尤其是其中next数组的含义
简单来说,它表示的是这个位置的前一位置到最开头的那一串的前缀后缀最大值
不会kmp?请看这位大神的博客  http://blog.csdn.net/v_july_v/article/details/7041827 写的非常详细透彻,我也是看过才会的

假设你会了
先注意一下,这个题目要求的子串全部是从头开始的,这就非常符合kmp算法
那在这个基础上,就可以进行一次dp,这个方程是通过next数组的基本含义所发现然后推出来的
我们假设当前走到的位置是i,那么它表示的是从0到 i-1这个串的前缀后缀最大值
令j=next[i],j表示前缀后缀最大值,j-1就表示最长前缀后缀的尾指针,i-1表示的是我现在位置的前一个位置,也就是现在这个串(0..i-1)的尾指针
设ans[i] 表示0 到 i 这个串前缀后缀的总数
你会不难发现,ans[i-1]和ans[j-1]之间有些关系(请注意i和i-1,j和j-1分别表示什么)
ans[i-1]=ans[j-1]+1;
也就是说,从0到j-1这个串的所有前缀后缀都在0到i-1这个串中出现一次。而 +1,代表算上0到j-1这个串
最后加上一下所有的ans[i] (0=<i<n) ,完了么?没有,因为每个串本身还要算一次,所有最后答案加上n即可,不要忘记中间取模和最后取一次模

如果觉得我讲的很迷,就你就按照next数组的思想去想dp方程吧。
前提是一定要把kmp理解透彻


//Hello. I'm Peter.
#include<cstdio>
#include<iostream>
#include<sstream>
#include<cstring>
#include<string>
#include<cmath>
#include<cstdlib>
#include<algorithm>
#include<functional>
#include<cctype>
#include<ctime>
#include<stack>
#include<queue>
#include<vector>
#include<set>
#include<map>
using namespace std;
typedef long long ll;
typedef long double ld;
#define peter cout<<"i am peter"<<endl
#define input freopen("data.txt","r",stdin)
#define randin srand((unsigned int)time(NULL))
#define INT (0x3f3f3f3f)*2
#define LL (0x3f3f3f3f3f3f3f3f)*2
#define gsize(a) (int)a.size()
#define len(a) (int)strlen(a)
#define slen(s) (int)s.length()
#define pb(a) push_back(a)
#define clr(a) memset(a,0,sizeof(a))
#define clr_minus1(a) memset(a,-1,sizeof(a))
#define clr_INT(a) memset(a,INT,sizeof(a))
#define clr_true(a) memset(a,true,sizeof(a))
#define clr_false(a) memset(a,false,sizeof(a))
#define clr_queue(q) while(!q.empty()) q.pop()
#define clr_stack(s) while(!s.empty()) s.pop()
#define rep(i, a, b) for (int i = a; i < b; i++)
#define dep(i, a, b) for (int i = a; i > b; i--)
#define repin(i, a, b) for (int i = a; i <= b; i++)
#define depin(i, a, b) for (int i = a; i >= b; i--)
#define pi 3.1415926535898
#define eps 1e-6
#define MOD 10007
#define MAXN
#define N 200100
#define M
int nextpos[N],n,ans[N],lastans;
//nextpos是kmp算法中的next数组
//ans表示从0到i这个串中前缀后缀总数,就是答案的一部分
char s[N];
void build_nextpos()
{//kmp算法中最重要的next数组
    int i,j;
    i=0;
    j=-1;
    nextpos[0]=-1;
    while(i<n)
    {
        if(j==-1 || s[i]==s[j])
        {
            nextpos[i+1]=j+1;
            i++;
            j=j+1;
        }
        else j=nextpos[j];
    }
}
int main()
{
    int T,j;
    cin>>T;
    while(T--)
    {
        scanf("%d %s",&n,s);
        repin(i,0,n)
        {
            ans[i]=0;
        }
        build_nextpos();
        repin(i,0,n)
        {
            j=nextpos[i];
            if(j>0 && i>0)
            {//dp方程
                ans[i-1]=ans[j-1]+1;
                ans[i-1]%=MOD;
            }
        }
        lastans=0;
        rep(i,0,n)
        {//最后统计答案
            lastans+=ans[i];
            lastans%=MOD;
        }
        //由于每个串本身还要算一次,所以+n
        lastans+=n;
        lastans%=MOD;
        printf("%d\n",lastans);
    }
}


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值