後綴數組學習資料:http://blog.csdn.net/wxfwxf328/article/details/7599929php
題目連接:http://acm.hdu.edu.cn/showproblem.php?pid=4691ios
思路:首先求出上下相鄰的查詢的字符串的LCP(最長公共前綴),這個能夠經過求出的height數組獲得。咱們能夠先求出上下相鄰的字符串的rank,而後對height數組作RQM就能夠(固然能夠先預處理,而後查詢的複雜度就爲O(1)啦。數組
#include <iostream> #include <cstdio> #include <cstring> #include <cmath> #include <algorithm> #include <vector> #include <queue> using namespace std; const int MAX_N = (100000 + 10000); const int inf = 0x3f3f3f3f; struct SA { /* rank[] 數組中rank[0] ~ rank[n - 1]爲有效值,rank[n]必爲0(字符串最後一位補0). sa[] 數組中sa[1] ~ sa[n]爲有效值,sa[0] 爲 n一定爲無效值 height[] 數組中有效值爲height[2] ~ height[n]. */ int wa[MAX_N], wb[MAX_N], wv[MAX_N], ws[MAX_N]; void da(int *r, int *sa, int n, int m) { int i, j, p, *x = wa, *y = wb; //第一輪基數排序 for (i = 0; i < m; ++i) ws[i] = 0; for (i = 0; i < n; ++i) ws[ x[i] = r[i] ]++; for (i = 1; i < m; ++i) ws[i] += ws[i - 1]; for (i = n - 1; i >= 0; --i) sa[--ws[x[i]]] = i; for (j = 1, p = 1; p < n; j <<= 1, m = p) { for (p = 0, i = n - j; i < n; ++i) y[p++] = i; //從n - j到j的字符串的第二關鍵字都是0,所以在y數組中排在前面 //上一行中只有sa[i] >= j的第sa[i]個字符串的Rank纔會做爲下一行的第sa[i] - j個字符串的第二關鍵字 //而且是按sa[i]的順序Rank[sa[i]]遞增的,所以完成了對剩餘元素的第二關鍵字的排序 for (i = 0; i < n; ++i) if (sa[i] >= j) y[p++] = sa[i] - j; //y數組是按照第二關鍵字排序的結果 //按照第一關鍵字進行基數排序 for (i = 0; i < n; ++i) wv[i] = x[y[i]]; for (i = 0; i < m; ++i) ws[i] = 0; for (i = 0; i < n; ++i) ws[wv[i]]++; for (i = 1; i < m; ++i) ws[i] += ws[i - 1]; for (i = n - 1; i >= 0; --i) sa[--ws[wv[i]]] = y[i]; swap(x, y); //p指的不一樣Rank的字符串的個數 for (p = 1, x[sa[0]] = 0, i = 1; i < n; ++i) { x[sa[i]] = cmp(y, sa[i - 1], sa[i], j) ? p - 1 : p++; } } } int cmp(int *r, int a, int b, int l) { return (r[a] == r[b] && r[a + l] == r[b + l]); } //求Rank數組以及height數組 //定義height[i]爲suffix(sa[i - 1])和suffix(sa[i])的最長公共前綴(LCP); //對於任意起始位置的i,j(假設Rank[i] < Rank[j])的後綴的最長公共前綴,爲height[Rank[i]+1],height[Rank[i] + 2],...,height[Rank[j]]的最小值 int sa[MAX_N], Rank[MAX_N], height[MAX_N]; void calheight(char *str, int *r, int *sa, int n) { int k = 0; for (int i = 0; i <= n; ++i) r[sa[i]] = i; for (int i = 0; i < n; ++i) { if (k) k--; int j = sa[r[i] - 1]; //若是k不爲0,則最長公共前綴至少爲k - 1; //k == 0, 則直接比較第i個字符串和第j個字符串有多少相同的 while (str[i + k] == str[j + k]) k++; height[r[i]] = k; } } } m_sa; int dp[MAX_N][22]; void InitRMQ(int n) { for (int i = 0; i <= n; ++i) dp[i][0] = m_sa.height[i]; for (int j = 1; (1 << j) <= n; ++j) { for (int i = 1; i + (1 << j) - 1 <= n; ++i) { dp[i][j] = min(dp[i][j - 1], dp[i + (1 << (j - 1))][j - 1]); } } } int RMQ_ST(int l, int r) { int k = (int)(log(1.0 * r - l + 1) / log(2.0)); return min(dp[l][k], dp[r - (1 << k) + 1][k]); } int getLCP(int a, int b) { a = m_sa.Rank[a]; b = m_sa.Rank[b]; //printf("a = %d, b = %d\n", a, b); if (a > b) swap(a, b); return RMQ_ST(a + 1, b); } int Cal(int n) { if (n == 0) return 1; int cnt = 0; while (n) { ++cnt; n /= 10; } return cnt; } char str[MAX_N]; int lpos[MAX_N], rpos[MAX_N]; int main() { while (~scanf("%s", str)) { int len = strlen(str); for (int i = 0; i < len; ++i) m_sa.Rank[i] = str[i] - 'a' + 1; m_sa.Rank[len] = 0; m_sa.da(m_sa.Rank, m_sa.sa, len + 1, 30); m_sa.calheight(str, m_sa.Rank, m_sa.sa, len); /* puts("Rank.."); for (int i = 0; i < len; ++i) { printf("%d ", m_sa.Rank[i]); } puts("\nHeight..."); for (int i = 0; i <= len; ++i) { printf("%d ", m_sa.height[i]); } puts(""); */ InitRMQ(len); int Cas; scanf("%d", &Cas); __int64 ans1 = 0, ans2 = 0; int tmp; for (int i = 0; i < Cas; ++i) { scanf("%d %d", &lpos[i], &rpos[i]); if (i == 0) { ans1 += rpos[i] - lpos[i] + 1; ans2 += rpos[i] - lpos[i] + 3; continue; } if (lpos[i] != lpos[i - 1]) tmp = getLCP(lpos[i - 1], lpos[i]); else tmp = inf; //printf("tmp = %d\n", tmp); tmp = min(tmp, rpos[i] - lpos[i]); tmp = min(tmp, rpos[i - 1] - lpos[i - 1]); ans1 += rpos[i] - lpos[i] + 1; ans2 += rpos[i] - lpos[i] - tmp + 1; ans2 += 1; ans2 += Cal(tmp); } printf("%I64d %I64d\n", ans1, ans2); } return 0; }
題目連接:http://acm.hdu.edu.cn/showproblem.php?pid=4552學習
思路:題目的意思就是求字符串全部的前綴出現的次數之和,其實能夠轉化爲全部後綴與該字符串的最長公共前綴之和。spa
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> using namespace std; const int MAX_N = (100000 + 10000); struct suffix_sa { int wa[MAX_N], wb[MAX_N], wv[MAX_N], ws[MAX_N]; void da(int *r, int *sa, int n, int m) { int i, j, p, *x = wa, *y = wb; for (i = 0; i < m; ++i) ws[i] = 0; for (i = 0; i < n; ++i) ws[ x[i] = r[i] ]++; for (i = 1; i < m; ++i) ws[i] += ws[i - 1]; for (i = n - 1; i >= 0; --i) sa[--ws[x[i]]] = i; for (j = 1, p = 1; p < n; j <<= 1, m = p) { for (p = 0, i = n - j; i < n; ++i) y[p++] = i; for (i = 0; i < n; ++i) if (sa[i] >= j) y[p++] = sa[i] - j; for (i = 0; i < n; ++i) wv[i] = x[y[i]]; for (i = 0; i < m; ++i) ws[i] = 0; for (i = 0; i < n; ++i) ws[wv[i]]++; for (i = 1; i < m; ++i) ws[i] += ws[i - 1]; for (i = n - 1; i >= 0; --i) sa[--ws[wv[i]]] = y[i]; swap(x, y); for(p = 1, x[sa[0]] = 0, i = 1; i < n; ++i) { x[sa[i]] = cmp(y, sa[i - 1], sa[i], j) ? p - 1 : p++; } } } int cmp(int *r, int a, int b, int l) { return (r[a] == r[b] && r[a + l] == r[b + l]); } int sa[MAX_N], Rank[MAX_N], height[MAX_N]; void calheight(char *str, int *r, int *sa, int n) { for (int i = 0; i <= n; ++i) r[sa[i]] = i; int k = 0; for (int i = 0; i < n; ++i) { if (k) k--; int j = sa[r[i] - 1]; while (str[i + k] == str[j + k]) k++; height[r[i]] = k; } } } m_sa; char str[MAX_N]; int main() { while (~scanf("%s", str)) { int len = strlen(str); for (int i = 0; i < len; ++i) m_sa.Rank[i] = str[i] - 'a' + 1; m_sa.Rank[len] = 0; m_sa.da(m_sa.Rank, m_sa.sa, len + 1, 30); m_sa.calheight(str, m_sa.Rank, m_sa.sa, len); /* printf("Rank...\n"); for (int i = 0; i <= len; ++i) { printf("%d ", m_sa.Rank[i]); } puts("\nHeight..."); for (int i = 0; i <= len; ++i) { printf("%d ", m_sa.height[i]); } puts(""); */ int ans = len, tmp = len; for (int i = m_sa.Rank[0] + 1; i <= len; ++i) { tmp = min(tmp, m_sa.height[i]); ans += tmp; ans %= 256; } tmp = len; for (int i = m_sa.Rank[0]; i >= 0; --i) { tmp = min(tmp, m_sa.height[i]); ans += tmp; ans %= 256; } printf("%d\n", ans); } return 0; }
題目連接:http://acm.hdu.edu.cn/showproblem.php?pid=3518.net
思路:題目的意思是要求給定字符串中至少出現兩次而且不重疊的子串的個數。能夠這麼理解,若是某個子串知足這個條件,那麼該子串至少是某兩個後綴的公共前綴,可是須要注意的是要不重疊,因而還需再判斷一下這兩個後綴的起始位置距離是否大於這個子串的長度便可。因而咱們能夠枚舉子串的長度,而後利用height數組求解。code
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> using namespace std; const int MAX_N = (1000 + 100); struct suffix_sa { int wa[MAX_N], wb[MAX_N], ws[MAX_N], wv[MAX_N]; void da(int *r, int *sa, int n, int m) { int i, j, p, *x =wa, *y = wb; for (i = 0; i < m; ++i) ws[i] = 0; for (i = 0; i < n; ++i) ws[ x[i] = r[i] ]++; for (i = 1; i < m; ++i) ws[i] += ws[i - 1]; for (i = n - 1; i >= 0; --i) sa[--ws[x[i]]] = i; for (j = 1, p = 1; p < n; j <<= 1, m = p) { for (p = 0, i = n - j; i < n; ++i) y[p++] = i; for (i = 0; i < n; ++i) if (sa[i] >= j) y[p++] = sa[i] - j; for (i = 0; i < n; ++i) wv[i] = x[y[i]]; for (i = 0; i < m; ++i) ws[i] = 0; for (i = 0; i < n; ++i) ws[wv[i]]++; for (i = 1; i < m; ++i) ws[i] += ws[i - 1]; for (i = n - 1; i >= 0; --i) sa[--ws[wv[i]]] = y[i]; swap(x, y); for (p = 1, x[sa[0]] = 0, i = 1; i < n; ++i) { x[sa[i]] = cmp(y, sa[i - 1], sa[i], j) ? p - 1 : p++; } } } int cmp(int *r, int a, int b, int l) { return (r[a] == r[b] && r[a + l] == r[b + l]); } int sa[MAX_N], Rank[MAX_N], height[MAX_N]; void calheight(char *str, int *r, int n) { for (int i = 0; i <= n; ++i) r[sa[i]] = i; int k = 0; for (int i = 0; i < n; ++i) { if (k) k--; int j = sa[r[i] - 1]; while (str[i + k] == str[j + k]) k++; height[r[i]] = k; } } } m_sa; char str[MAX_N]; int main() { while (~scanf("%s", str) && strcmp(str, "#") != 0) { int n = strlen(str); for (int i = 0; i < n; ++i) m_sa.Rank[i] = str[i] - 'a' + 1; m_sa.Rank[n] = 0; m_sa.da(m_sa.Rank, m_sa.sa, n + 1, 30); m_sa.calheight(str, m_sa.Rank, n); /* for (int i = 0; i <= n; ++i) { printf("%d ", m_sa.Rank[i]); } puts(""); for (int i = 1; i <= n; ++i) { printf("%d ", m_sa.height[i]); } puts(""); */ __int64 ans = 0; for (int len = 1; len <= (n + 1) / 2; ++len) { int l = MAX_N, r = -1; for (int i = 1; i <= n; ++i) { if (m_sa.height[i] >= len) { l = min(l, min(m_sa.sa[i - 1], m_sa.sa[i])); r = max(r, max(m_sa.sa[i - 1], m_sa.sa[i])); } else { if (l + len <= r) ++ans; l = MAX_N, r = -1; } } if (r != -1 && l + len <= r) ++ans; } printf("%d\n", ans); } return 0; }
題目連接:http://acm.hdu.edu.cn/showproblem.php?pid=1403blog
思路:求兩個字符串的最長公共子串,能夠將這兩個字符串收尾拼接在一塊兒,而後就是求新字符串全部後綴的最長公共前綴。排序
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> using namespace std; const int MAX_N = (233333); struct suffix_sa { int wa[MAX_N], wb[MAX_N], ws[MAX_N], wv[MAX_N]; void da(int *r, int *sa, int n, int m) { int i, j, p, *x = wa, *y = wb; for (i = 0; i < m; ++i) ws[i] = 0; for (i = 0; i < n; ++i) ws[ x[i] = r[i] ]++; for (i = 1; i < m; ++i) ws[i] += ws[i - 1]; for (i = n - 1; i >= 0; --i) sa[--ws[x[i]]] = i; for (j = 1, p = 1; p < n; j <<= 1, m = p) { for (p = 0, i = n - j; i < n; ++i) y[p++] = i; for (i = 0; i < n; ++i) if (sa[i] >= j) y[p++] = sa[i] - j; for (i = 0; i < n; ++i) wv[i] = x[y[i]]; for (i = 0; i < m; ++i) ws[i] = 0; for (i = 0; i < n; ++i) ws[wv[i]]++; for (i = 1; i < m; ++i) ws[i] += ws[i - 1]; for (i = n - 1; i >= 0; --i) sa[--ws[wv[i]]] = y[i]; swap(x, y); for (p = 1, x[sa[0]] = 0, i = 1; i < n; ++i) { x[sa[i]] = cmp(y, sa[i - 1], sa[i], j) ? p - 1 : p++; } } } int cmp(int *r, int a, int b, int l) { return (r[a] == r[b] && r[a + l] == r[b + l]); } int sa[MAX_N], Rank[MAX_N], height[MAX_N]; void calheight(char *str, int *r, int n) { for (int i = 0; i <= n; ++i) r[sa[i]] = i; int k = 0; for (int i = 0; i < n; ++i) { if (k) k--; int j = sa[r[i] - 1]; while (str[i + k] == str[j + k]) k++; height[r[i]] = k; } } } m_sa; char str1[MAX_N], str2[MAX_N]; int main() { while (~scanf("%s %s", str1, str2)) { int n1 = strlen(str1), n2 = strlen(str2), n; for (int i = 0; i < n2; ++i) str1[n1 + i] = str2[i]; n = n1 + n2; for (int i = 0; i < n; ++i) m_sa.Rank[i] = str1[i] - 'a' + 1; m_sa.Rank[n] = 0; m_sa.da(m_sa.Rank, m_sa.sa, n + 1, 30); m_sa.calheight(str1, m_sa.Rank, n); /* for (int i = 0; i <= n; ++i) { printf("%d ", m_sa.Rank[i]); } puts(""); for (int i = 0; i <= n; ++i) { printf("%d ", m_sa.height[i]); } puts(""); */ int ans = 0; for (int i = 1; i <= n; ++i) { if ((m_sa.sa[i - 1] < n1 && m_sa.sa[i] >= n1) || (m_sa.sa[i - 1] >= n1 && m_sa.sa[i] < n1)) { ans = max(ans, m_sa.height[i]); } } printf("%d\n", ans); } return 0; }更新中。。。