KMP算法Next()函数的一个应用

记一个KMP算法的应用,经典的KMP算法详解还是看这里

问题:给一个串,求这个串前i位构成的前缀由多少个子串组成。

比如aabaabaabaab,前2位是aa,a重复了2次,前6位是aabaab,aab重复了2次,前9位是aabaabaab,aab重复了3次,前12位是aabaabaabaab,aab重复了4次。

先说一下next()函数。pre[i] = j表示   S[1...j] = S[i - j....i];

下面讨论当i % (i - pre[i]) == 0 时,例如i = 12, pre[12] = 9:

如图。

S[1...9] == S[3...12];

因为已知 i % (i - pre[i]) == 0; 那么把i分成 i / (i - pre[i])段。

已知:

s3 == t3;

s2 == t2;

s1 == t1;

又因为t3 == s2, t1 == s1。所以 t1 = t2 = t3 = s1 = s2 = s3,也就是说 i / (i - pre[i])这几段中每一段都相等。

现在回到原问题:求这个串前i位构成的前缀由多少个子串组成,只需要找到i / (i - pre[i]) == 0的点i,则共有 i / (i - pre[i])个相同的子串构成前缀S[1...i]。

 

练习:POJ 1961POJ 2406

 

附1961的代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <stack>
#include <cmath>
#include <algorithm>


#define CL(arr, val)    memset(arr, val, sizeof(arr))
#define REP(i, n)       for(i = 0; i < n; ++i)
#define FOR(i, l, h)    for(i = l; i <= h; ++i)
#define FORD(i, h, l)   for(i = h; i >= l; --i)
#define L(x)    x << 1
#define R(x)    x << 1 | 1
#define MID(l, r)  (l + r) >> 1
typedef long long LL;

using namespace std;

const int N = 1000010;

char s[N];
int pre[N];
int dp[N];    //这里加了一个数组,记录到i位置时所构成的前缀由多少个子串组成。
int n;

void Next() {
    int i, j = -1;
    pre[0] = -1;
    for(i = 1; i < n; ++i) {
        while(j > -1 && s[j+1] != s[i]) j = pre[j];
        if(s[j+1] == s[i])  ++j;
        pre[i] = j;
    }
}

int main() {
    freopen("data.in", "r", stdin);

    int i, cas = 0;
    while(scanf("%d", &n), n) {
        scanf("%s", s);
        printf("Test case #%d\n", ++cas);
        Next();
        REP(i, n + 1)   dp[i] = 1; 
        FOR(i, 1, n - 1) {
            if((i + 1) % (i - pre[i]) == 0 && pre[i] != -1) {
                dp[i] = dp[pre[i]] + 1;    //到i的前缀就等于到pre[i]的前缀子串数加上 [pre[i], i]这个子串。
                printf("%d %d\n", i + 1, dp[i]);    //其实直接输出(i + 1)/(i - pre[i])就行,这里写搓了。。。T_T
            }
        }
        cout << endl;
    }
    return 0;
}

 

 

 

 

posted @ 2012-05-04 21:32  AC_Von  阅读(2142)  评论(3编辑  收藏  举报