POJ 3415 后缀数组

链接:

http://poj.org/problem?id=3415

题意:

统计A和B长度不小于K的公共子串个数。

题解:

将A和B拼接后,利用单调栈累计分属两者的后缀对应的LCP-K+1即为答案

代码:

 31 int n, k;
 32 int Rank[MAXN], tmp[MAXN];
 33 int sa[MAXN], lcp[MAXN];
 34
 35 bool compare_sa(int i, int j) {
 36     if (Rank[i] != Rank[j]) return Rank[i] < Rank[j];
 37     else {
 38         int ri = i + k <= n ? Rank[i + k] : -1;
 39         int rj = j + k <= n ? Rank[j + k] : -1;
 40         return ri < rj;
 41     }
 42 }
 43
 44 void construct_sa(string S, int *sa) {
 45     n = S.length();
 46     for (int i = 0; i <= n; i++) {
 47         sa[i] = i;
 48         Rank[i] = i < n ? S[i] : -1;
 49     }
 50     for (k = 1; k <= n; k *= 2) {
 51         sort(sa, sa + n + 1, compare_sa);
 52         tmp[sa[0]] = 0;
 53         for (int i = 1; i <= n; i++)
 54             tmp[sa[i]] = tmp[sa[i - 1]] + (compare_sa(sa[i - 1], sa[i]) ? 1 : 0);
 55         for (int i = 0; i <= n; i++) Rank[i] = tmp[i];
 56     }
 57 }
 58
 59 void construct_lcp(string S, int *sa, int *lcp) {
 60     int n = S.length();
 61     for (int i = 0; i <= n; i++) Rank[sa[i]] = i;
 62     int h = 0;
 63     lcp[0] = 0;
 64     for (int i = 0; i < n; i++) {
 65         int j = sa[Rank[i] - 1];
 66         if (h > 0) h--;
 67         for (; j + h < n && i + h < n; h++)
 68             if (S[j + h] != S[i + h]) break;
 69         lcp[Rank[i] - 1] = h;
 70     }
 71 }
 72
 73 int st[MAXN][2];
 74 ll contribution, top;
 75 using namespace std;
 76
 77 ll solve(int k, int n1, bool is_s1) {
 78     ll ans = 0;
 79     rep(i, 0, n) {
 80         if (lcp[i] < k) {
 81             top = contribution = 0;
 82             continue;
 83         }
 84         int size = 0;
 85         if (is_s1 && sa[i] < n1 || !is_s1 && sa[i] > n1) {
 86             ++size;
 87             contribution += lcp[i] - k + 1;
 88         }
 89         while (top > 0 && lcp[i] <= st[top - 1][0]) {
 90             --top;
 91             contribution -= st[top][1] * (st[top][0] - lcp[i]);
 92             size += st[top][1];
 93         }
 94         if (size) {
 95             st[top][0] = lcp[i];
 96             st[top][1] = size;
 97             ++top;
 98         }
 99         if (is_s1 && sa[i + 1] > n1 || !is_s1 && sa[i + 1] < n1) ans += contribution;
100     }
101     return ans;
102 }
103
104 int main() {
105     ios::sync_with_stdio(false), cin.tie(0);
106     int k;
107     while (cin >> k, k) {
108         string a, b;
109         cin >> a >> b;
110         int n1 = a.length();
111         string s = a + ‘$‘ + b;
112         construct_sa(s, sa);
113         construct_lcp(s, sa, lcp);
114         cout << solve(k, n1, true) + solve(k, n1, false) << endl;
115     }
116     return 0;
117 }
时间: 2024-08-05 22:04:32

POJ 3415 后缀数组的相关文章

poj 3415 后缀数组分组+排序+并查集

Source Code Problem: 3415   User: wangyucheng Memory: 16492K   Time: 704MS Language: C++   Result: Accepted Source Code #include<iostream> #include<cstdio> #include<algorithm> #include<cstring> using namespace std; #define N 510000

POJ 3415:后缀数组+单调栈优化

题意很简单,求两个字符串长度大于等于K的子串个数 一开始还是只会暴力..发现n^2根本没法做...看了题解理解了半天才弄出来,太弱了... 思路:把两个字符串连接后做一次后缀数组,求出height 暴力的想法自然是枚举第一个子串的起始位置i和第二个子串的起始位置j,肯定会T的 看了题解才知道有单调栈这种有优化方法.. 将后缀分为A组(起始点为第一个字符串).B组 设符合要求的lcp长度为a,则其对答案的贡献为a-k+1(长度为k~a的都是符合要求的) 一开始这里我也是有疑问的,比如说k=1,aa

POJ 3415 后缀数组+单调栈

题目大意: 给定A,B两种字符串,问他们当中的长度大于k的公共子串的个数有多少个 这道题目本身理解不难,将两个字符串合并后求出它的后缀数组 然后利用后缀数组求解答案 这里一开始看题解说要用栈的思想,觉得很麻烦就不做了,后来在比赛中又遇到就后悔了,到今天看了很久才算看懂 首先建一个栈,从栈底到栈顶都保证是单调递增的 我们用一个tot记录当前栈中所有项和一个刚进入的子串匹配所能得到的总的子串的数目(当然前提是,当前进入的子串height值比栈顶还大,那么和栈中任意一个子串匹配都保持当前栈中记录的那时

POJ 2774 后缀数组:求最长公共子串

思路:其实很简单,就是两个字符串连接起来,中间用个特殊字符隔开,然后用后缀数组求最长公共前缀,然后不同在两个串中,并且最长的就是最长公共子串了. 注意的是:用第一个字符串来判断是不是在同一个字符中,刚开始用了第二个字符的长度来判断WA了2发才发现. #include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<map> #include<

poj 3261 后缀数组 找重复出现k次的子串(子串可以重叠)

题目:http://poj.org/problem?id=3261 仍然是后缀数组的典型应用----后缀数组+lcp+二分 做的蛮顺的,1A 但是大部分时间是在调试代码,因为模板的全局变量用混了,而自己又忘了,,,等西安邀请赛还有四省赛结束之后,该冷静反思下尝试拜托模板了 错误   :1.k用错,题目的k和模板的k用混; 2.还是二分的C()函数,这个其实跟前一篇<poj 1226 hdu 1238 Substrings 求若干字符串正串及反串的最长公共子串 2002亚洲赛天津预选题>的C函数

POJ 1226后缀数组:求出现或反转后出现在每个字符串中的最长子串

思路:这题是论文里的最后一道练习题了,不过最后一题竟然挺水的. 因为求的是未反转或者反转后,最长公共子串. 刚开始还真不知道怎么构建连接成一个字符串,因为需要有反转嘛! 但是其实挺简单的,把未反转的和反转后的字符串都连起来,中间用未出现过的字符隔开就行了!然后未反转的和反转的在同一组. 二分枚举最长的公共前缀长度,然后统计看看这个最长的长度在不在所有的组里,如果在就符合-- #include<iostream> #include<cstdio> #include<cstrin

POJ 3294 后缀数组:求不小于k个字符串中的最长子串

思路:先把所有的串连接成一个串,串写串之前用没出现过的字符隔开,然后求后缀:对height数组分组二分求得最长的公共前缀,公共前缀所在的串一定要是不同的,不然就不是所有串的公共前缀了,然后记下下标和长度即可. 刚开始理解错题意,然后不知道怎么写,然后看别人题解也不知道怎么意思,后面看了好久才知道题目意思理解错了. 时间四千多ms,别人才一百多ms,不知道别人怎么做的-- #include<iostream> #include<cstdio> #include<cstring&

POJ 1743 后缀数组:求最长不重叠子串

数据:这题弄了好久,WA了数十发,现在还有个例子没过,可却A了,POJ 的数组也太弱了. 10 1 1 1 1 1 1 1 1 1 1 这组数据如果没有那个n-1<10判断的话,输入的竟然是5,我靠-- 思路:这个题目关键的地方有两个:第一,重复的子串一定可以看作是某两个后缀的公共前缀,第二,把题目转化成去判定对于任意的一个长度k,是否存在长度至少为k的不重叠的重复的子串. 转化成判定问题之后,就可以二分去解答了.在验证判定是否正确时,我们可以把相邻的所有不小于k的height[]看成一组,然后

POJ 1226 后缀数组

题目链接:http://poj.org/problem?id=1226 题意:给定n个字符串[只含大小写字母],求一个字符串要求在n个串或者他们翻转后的串的出现过.输出满足要求的字符串的长度 思路:根据<<后缀数组——处理字符串的有力工具>>的思路,这题不同的地方在于要判断是否在反转后的字符串中出现 .其实这并没有加大题目的难度 . 只需要先将每个字符串都反过来写一遍, 中间用一个互不相同的且没有出现在字符串中的字符隔开,再将 n个字符串全部连起来, 中间也是用一 个互不相同的且没