CodeForces 427D Match & Catch 后缀数组
2017-12-04 11:06
232 查看
题目:https://cn.vjudge.net/problem/CodeForces-427D
题意:给出两个字符串,求最小公共子串长度,使这个子串仅在两个串均出现一次。
思路:将这两个字符串拼成一个串,中间用一个没出现过的字符隔开(我用的空格),对合成的串求后缀数组,这样两个数组的公共子串所在的后缀在sa中应该是相邻的。通过观察,只有当这两个sa中相邻的后缀sa[i]和sa[i-1]左右的后缀都不含这个子串时,才能保证子串在两个串都只出现一次。
具体来说就是
(1)后缀sa[i]和sa[i-1]分别属于两个字符串
(2)height[i-1] < height[i] && height[i+1] < height[i]
(3)这个最小公共子串长度就是max(height[i-1], height[i+1]) + 1
整体扫过一遍height数组统计最小值即可,没有满足的情况时输出-1
代码:c++
题意:给出两个字符串,求最小公共子串长度,使这个子串仅在两个串均出现一次。
思路:将这两个字符串拼成一个串,中间用一个没出现过的字符隔开(我用的空格),对合成的串求后缀数组,这样两个数组的公共子串所在的后缀在sa中应该是相邻的。通过观察,只有当这两个sa中相邻的后缀sa[i]和sa[i-1]左右的后缀都不含这个子串时,才能保证子串在两个串都只出现一次。
具体来说就是
(1)后缀sa[i]和sa[i-1]分别属于两个字符串
(2)height[i-1] < height[i] && height[i+1] < height[i]
(3)这个最小公共子串长度就是max(height[i-1], height[i+1]) + 1
整体扫过一遍height数组统计最小值即可,没有满足的情况时输出-1
代码:c++
#include <cstdio> #include <iostream> #include <cstring> #include <string> #include <algorithm> #include <cmath> using namespace std; const int maxn = 20000; const int INF = 2147483647; char s[maxn]; int sa[maxn], t[maxn], t2[maxn], c[maxn]; int n; //字符串s的下标从1到n,且s[0]必须为空字符 //构造字符串s的后缀数组。每个字符ASCII值必须在0~m-1范围内 void build_sa(int m, int n) { n++; int *x = t; int *y = t2; //基数排序 for (int i = 0; i < m; i++) { c[i] = 0; } for (int i = 0; i < n; i++) { x[i] = s[i]; c[x[i]]++; } for (int i = 1; i < m; i++) { c[i] += c[i - 1]; } for (int i = n - 1; i >= 0; i--) { sa[--c[x[i]]] = i; } for (int k = 1; k <= n; k <<= 1) { int p = 0; //直接利用sa排序第二关键字 for (int i = n - k; i < n; i++) { y[p++] = i; } for (int i = 0; i < n; i++) { if (sa[i] >= k) { y[p++] = sa[i] - k; } } //基数排序第一关键字 for (int i = 0; i < m; i++) { c[i] = 0; } for (int i = 0; i < n; i++) { c[x[y[i]]]++; } for (int i = 0; i < m; i++) { b3c5 c[i] += c[i - 1]; } for (int i = n - 1; i >= 0; i--) { sa[--c[x[y[i]]]] = y[i]; } //根据sa和y数组计算新的x数组 swap(x, y); p = 1; x[sa[0]] = 0; for (int i = 1; i < n; i++) { if (y[sa[i - 1]] == y[sa[i]] && y[sa[i - 1] + k] == y[sa[i] + k]) { x[sa[i]] = p - 1; } else { x[sa[i]] = p++; } } //以后即使继续倍增,sa也不会改变 if (p >= n) { break; } m = p;//下次基数排序的最大值 } } int ranks[maxn], height[maxn]; //ranks[i]是后缀i的排名 //height[i]是后缀i和后缀i-1的最长公共前缀长度 void getHeight(int n) { n++; int k = 0; for (int i = 0; i < n; i++) { ranks[sa[i]] = i; } for (int i = 0; i < n; i++) { if (ranks[i] == 0) { height[0] = 0; continue; } // 第一个后缀的 LCP 为 0。 if (k) { k--; // 从 k - 1 开始推 } int j = sa[ranks[i] - 1]; while (s[i + k] == s[j + k] && i + k < n && j + k < n) { k++; } height[ranks[i]] = k; } } int belong[maxn];//0 - 空字符,1 - 第一个串,2 - 第二个串 void init() { scanf("%s", s + 1); n = strlen(s + 1); for (int i = 1; i <= n; i++) { belong[i] = 1; } s[++n] = ' '; scanf("%s", &s[n + 1]); int st2 = n + 1; n += strlen(&s[n + 1]); for (int i = st2; i <= n; i++) { belong[i] = 2; } build_sa(200, n); getHeight(n); } int solve() { int minn = INF; for (int i = 1; i <= n; i++) { if (belong[sa[i - 1]] + belong[sa[i]] == 3) { if (height[i - 1] < height[i] && height[i + 1] < height[i]) { minn = min(minn, max(height[i - 1], height[i + 1]) + 1); } } } return minn == INF ? -1 : minn; } int main() { init(); printf("%d\n", solve()); return 0; }
相关文章推荐
- Codeforces 427D Match & Catch 后缀自动机 或 后缀数组
- Codeforces 427D Match & Catch 后缀自动机
- CodeForces 427 D.Match & Catch(后缀数组)
- Codeforces 427D Match & Catch(后缀自动机)
- cf 427D Match & Catch 后缀数组
- Codeforces 427D Match & Catch(后缀自动机)
- codeforces 427D Match & Catch(后缀数组,字符串)
- codeforces 757 A. Gotta Catch Em' All!
- Gotta Catch Em' All!_Codeforces
- codeforces 700E 后缀数组
- cf 427D Match & Catch 后缀数组
- Codeforces 757 Gotta Catch Em' All!
- CF 427D Match & Catch 求最短唯一连续LCS
- codeforces 873F(后缀数组)
- Codeforces 427 D. Match & Catch
- CodeForces 873F Forbidden Indices 后缀数组
- CodeForces-427D:Match & Catch (后缀自动机)
- Codeforces 427 D. Match & Catch
- CodeForces 427D-Match & Catch
- Codeforces 427 D. Match & Catch