poj 3415 :长度不小于 k 的公共子串的个数(后缀数组+单调栈)
2016-09-02 10:29
591 查看
题目:http://poj.org/problem?id=3415
题意:
给定两个字符串 A 和 B,求长度不小于 k 的公共子串的个数(可以相同)。 样例 1: A=“xx”,B=“xx”,k=1,长度不小于 k 的公共子串的个数是 5。 样例 2: A=“aababaa”,B=“abaabaa”,k=2,长度不小于 k 的公共子串的个数是22。
分析:
基本思路是计算 A 的所有后缀和 B 的所有后缀之间的最长公共前缀的长度,把最长公共前缀长度不小于 k 的部分全部加起来。先将两个字符串连起来,中间用一个没有出现过的字符隔开。按 height 值分组后,接下来的工作便是快速的统计每组中后缀之间的最长公共前缀之和。扫描一遍,每遇到一个 B 的后缀就统计与前面的 A 的后缀能产生多少个长度不小于 k 的公共子串,这里 A 的后缀需要用一个单调的栈来高效的维护。然后对 A 也这样做一次。
这题比较麻烦的是维护单调栈,一个比较简单的方法是栈中保存height值,维护height值单调递增,那么对于新加入的height值,sum保存前i个构成的公共子串数,如果height[i]<栈顶height值,说明(he[top] - height[i])这部分是多加上的,应该减去。因为对于后缀j和i,二者的最长公共前缀是min{height[rank[j]+1],height[rank[j]+1]..height[rank[i]] }
看到网上还有个不错的写法:http://blog.csdn.net/xingyeyongheng/article/details/16919251
题意:
给定两个字符串 A 和 B,求长度不小于 k 的公共子串的个数(可以相同)。 样例 1: A=“xx”,B=“xx”,k=1,长度不小于 k 的公共子串的个数是 5。 样例 2: A=“aababaa”,B=“abaabaa”,k=2,长度不小于 k 的公共子串的个数是22。
分析:
基本思路是计算 A 的所有后缀和 B 的所有后缀之间的最长公共前缀的长度,把最长公共前缀长度不小于 k 的部分全部加起来。先将两个字符串连起来,中间用一个没有出现过的字符隔开。按 height 值分组后,接下来的工作便是快速的统计每组中后缀之间的最长公共前缀之和。扫描一遍,每遇到一个 B 的后缀就统计与前面的 A 的后缀能产生多少个长度不小于 k 的公共子串,这里 A 的后缀需要用一个单调的栈来高效的维护。然后对 A 也这样做一次。
这题比较麻烦的是维护单调栈,一个比较简单的方法是栈中保存height值,维护height值单调递增,那么对于新加入的height值,sum保存前i个构成的公共子串数,如果height[i]<栈顶height值,说明(he[top] - height[i])这部分是多加上的,应该减去。因为对于后缀j和i,二者的最长公共前缀是min{height[rank[j]+1],height[rank[j]+1]..height[rank[i]] }
看到网上还有个不错的写法:http://blog.csdn.net/xingyeyongheng/article/details/16919251
#include<cstdio> #include<cstring> #include<algorithm> #include<iostream> using namespace std; typedef long long ll; const int INF = 1e9 + 9; const int N = 2e5 + 9; /********************倍增算法*后缀数组模板*******************************/ int sa , t1 , t2 , c , rk , height ; void build_sa (int s[], int n, int m) { int i, k, p, *x = t1, *y = t2; for (i = 0; i < m; i++) c[i] = 0; for (i = 0; i < n; i++) c[x[i] = s[i]]++; for (i = 1; i < m; i++) c[i] += c[i - 1]; for (i = n - 1; i >= 0; i--) sa[--c[x[i]]] = i; for (k = 1; k <= n; k <<= 1) { p = 0; for (i = n - k; i < n; i++) y[p++] = i; for (i = 0; i < n; i++) if (sa[i] >= k) y[p++] = sa[i] - k; for (i = 0; i < m; i++) c[i] = 0; for (i = 0; i < n; i++) c[x[y[i]]]++; for (i = 1; i < m; i++) c[i] += c[i - 1]; for (i = n - 1; i >= 0; i--) sa[--c[x[y[i]]]] = y[i]; swap (x, y); p = 1; x[sa[0]] = 0; for (i = 1; i < n; i++) x[sa[i]] = y[sa[i - 1]] == y[sa[i]] && y[sa[i - 1] + k] == y[sa[i] + k] ? p - 1 : p ++; if (p >= n) break; m = p; } } void getHeight (int s[], int n) { int i, j, k = 0; for (i = 0; i <= n; i++) rk[sa[i]] = i; for (i = 0; i < n; i++) { if (k) k--; j = sa[rk[i] - 1]; while (s[i + k] == s[j + k]) k++; height[rk[i]] = k; } } /********************************************************************************/ ll solve (int n, int len, int k) { int *cnt = t1, *he = t2, top = 0; //cnt保存当前栈he中每个height值的个数;he是栈,保存已经扫过的height值,当中元素时单调递增的,top栈顶指针 ll sum = 0, ans = 0; for (int i = 1; i <= n; i++) { if (height[i] < k) { sum = top = 0; continue; } int tot = 0; if (sa[i - 1] < len) { //如果sa[i-1]是A串,那么sum加上height[i]-k+1个sa[i]和sa[i-1]构成的公共子串个数 tot++; sum += height[i] - k + 1; } while (top > 0 && height[i] <= he[top - 1]) { top--; sum -= cnt[top] * (he[top] - height[i]); //因为栈顶的height值大于height[i],那么当前sa[i]跟之前的sa[j]就多加了 //cnt[top]*(he[top]-height[i])个子串,所以要减去(因为这些更长的后缀部分已经不能跟后面的后缀形成子串了。 tot += cnt[top]; } if (tot) { he[top] = height[i]; cnt[top++] = tot; } if (sa[i] > len) ans += sum; } for (int i = 1; i <= n; i++) { if (height[i] < k) { sum = top = 0; continue; } int tot = 0; if (sa[i - 1] > len) { tot++; sum += height[i] - k + 1; } while (top > 0 && height[i] <= he[top - 1]) { top--; sum -= cnt[top] * (he[top] - height[i]); tot += cnt[top]; } he[top] = height[i]; cnt[top++] = tot; if (sa[i] < len) ans += sum; } return ans; } int s , n, a , cas = 0, k; char str ; int main() { //freopen ("f.txt", "r", stdin); while (~scanf ("%d", &k) && k ) { scanf ("%s", str); int len = strlen (str); for (int i = 0; i < len; i++) s[i] = str[i]; s[len] = '#'; scanf ("%s", str + len + 1); int n = len; for (++n; str != '\0'; n++) s = str ; s = 0; build_sa (s, n + 1, 128); getHeight (s, n); cout << solve (n, len, k) << endl; } return 0; } /* Sample Input 2 aababaa abaabaa 1 xx xx 0 Sample Output 22 5 */
相关文章推荐
- POJ 3415 Common Substrings(长度不小于k 的公共子串的个数--后缀数组+单调栈优化)
- POJ - 3415 Common Substrings(后缀数组求长度不小于 k 的公共子串的个数+单调栈优化)
- POJ 3415 Common Substrings(长度不小于K的公共子串的个数+后缀数组+height数组分组思想+单调栈)
- POJ - 3415 Common Substrings(后缀数组求长度不小于 k 的公共子串的个数+单调栈优化)
- POJ 3415-Common Substrings(后缀数组+单调栈-公共子串的长度)
- (Relax 后缀数组1.3)POJ 3415 Common Substrings(求串A和串B中长度不小于k的公共子串数)
- poj 3415 后缀数组 两个字符串中长度不小于 k 的公共子串的个数
- 【POJ 3415】Common Substrings 长度不小于k的公共子串的个数
- POJ 3415 Common Substrings (求长度不小于k的公共子串的个数)
- POJ 3415 求两个字符串间长度不小于k的公共子串的个数
- 【poj3415-长度不小于k的公共子串个数】后缀数组+单调栈
- POJ 题目3415 Common Substrings(后缀数组+栈,求可以匹配到的长度大于k的公共子串个数)
- hdu 3415 后缀数组 长度不小于 k 的公共子串的个数
- poj 3415 长度超过K的公共子串个数
- POJ-Common Substrings(后缀数组-长度不小于 k 的公共子串的个数)
- POJ 3415 不小于k的公共子串的个数
- POJ 3415 Common Substrings (后缀数组,长度不小于k的公共子串的个数)
- POJ 3415 不小于k的公共子串的个数
- poj 3415 Common Substrings (后缀数组+单调栈)
- [POJ 3415] Common Substrings (后缀数组+单调栈优化)