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);
    }
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章