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]。
附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; }