CF 427D 后缀数组

大意是寻找两个字符串中最短的公共子串,要求子串在两个串中都是唯一的。

造一个S#T的串,做后缀数组,从小到大枚举子串长度在height数组中扫描,如果某一个组中来自两个串的数量分别为1,就找到了答案。

 1 #include <iostream>
 2 #include <vector>
 3 #include <algorithm>
 4 #include <string>
 5 #include <string.h>
 6 #include <stdio.h>
 7 #include <math.h>
 8 #include <stdlib.h>
 9 #include <queue>
10 #include <stack>
11 #include <map>
12 #include <set>
13 #include <ctime>
14 #include <numeric>
15 #include <cassert>
16
17 using namespace std;
18
19 const int N=10005;;
20
21 char s[N];
22
23 struct SuffixArray {
24     int wa[N], wb[N], cnt[N], wv[N];
25     int rk[N], height[N];
26     int sa[N];
27     bool cmp(int r[], int a, int b, int l) {
28         return r[a] == r[b] && r[a+l] == r[b+l];
29     }
30     void calcSA(char r[], int n, int m) {
31         int i, j, p, *x = wa, *y = wb;
32         for (i = 0; i < m; ++i) cnt[i] = 0;
33         for (i = 0; i < n; ++i) cnt[x[i]=r[i]]++;
34         for (i = 1; i < m; ++i) cnt[i] += cnt[i-1];
35         for (i = n-1; i >= 0; --i) sa[--cnt[x[i]]] = i;
36         for (j = 1, p = 1; p < n; j *= 2, m = p) {
37             for (p = 0, i = n - j; i < n; ++i) y[p++] = i;
38             for (i = 0; i < n; ++i) if (sa[i] >= j) y[p++] = sa[i] - j;
39             for (i = 0; i < n; ++i) wv[i] = x[y[i]];
40             for (i = 0; i < m; ++i) cnt[i] = 0;
41             for (i = 0; i < n; ++i) cnt[wv[i]]++;
42             for (i = 1; i < m; ++i) cnt[i] += cnt[i-1];
43             for (i = n-1; i >= 0; --i) sa[--cnt[wv[i]]] = y[i];
44             for (swap(x, y), p = 1, x[sa[0]] = 0, i = 1; i < n; ++i)
45                 x[sa[i]] = cmp(y, sa[i-1], sa[i], j) ? p-1 : p++;
46         }
47     }
48     void calcHeight(char r[], int n) {
49         int i, j, k = 0;
50         for (i = 0; i <= n; ++i) rk[sa[i]] = i;
51         for (i = 0; i < n; height[rk[i++]] = k)
52             for (k?k--:0, j = sa[rk[i]-1]; r[i+k] == r[j+k]; k++);
53     }
54     bool solve(int k,int n,int div) {
55         int ca=0,cb=0;
56         for (int i=1;i<=n;i++) {
57             if (height[i]<k) {
58                 if (ca==1&&cb==1)
59                     return true;
60                 ca=0;cb=0;
61                 if (sa[i]<div) ca++;
62                 else if (sa[i]>div) cb++;
63                 continue;
64             }
65             if (sa[i]<div) ca++;
66             else if (sa[i]>div) cb++;
67         }
68         return ca==1&&cb==1;
69     }
70 }suf;
71
72 char a[N],b[N];
73
74 int main(){
75     scanf("%s %s",a,b);
76     int n=strlen(a),m=strlen(b);
77     strcpy(s,a);
78     s[n]=‘#‘;
79     strcpy(s+n+1,b);
80     int tot=n+m+1;
81     suf.calcSA(s,tot+1,128);
82     suf.calcHeight(s,tot);
83     int ret=-1;
84     for (int i=1;i<=n;i++) {
85         if (suf.solve(i,tot,n)) {
86             ret=i;
87             break;
88         }
89     }
90     printf("%d\n",ret);
91     return 0;
92 }

时间: 2024-08-06 11:55:23

CF 427D 后缀数组的相关文章

CF 653F 后缀数组

大意是给出一个只包含'('和')'的括号串,求有多少不同的子串是合法的括号串 解法:对于每一个后缀,需要能够求出这个后缀有多少前缀是合法的括号串,这个可以用O(log n)复杂度的二分来解决.注意,二分的范围并不是整个后缀,因为如果将'('视作+1, ')'视作-1,则一个合法的括号串必须时刻不能小于0.所以可以在ST表上二分出合法范围,在这个范围内去统计有多少合法串(即'('与')'正负相消).求出一个区间内有多少数字值为x可以对一个保存值和位置的pair数组排序后二分. 那么剩下的问题就是如

CF(427D-Match &amp; Catch)后缀数组应用

题意:给两个字符串,求一个最短的子串.使得这个子串在两个字符串中出现的次数都等于1.出现的定义为:可以重叠的出现. 解法:后缀数组的应用.从小枚举长度.如果一个长度len合法的话:则一定存在这个样的sa[i]排名.sa[i]与s[i+1]的公共前缀长度大于等于len,且sa[i]与[i-1]的公共前缀长度小于len,同时sa[i+1]与[i+2]的公共前缀长度小于len,同时保证sa[i]与sa[i+1]在两个串中.Judge函数就是技巧性地实现了这些判断. 代码: #include<iostr

CF 1320D Reachable Strings 后缀数组

确定性解法:后缀数组 众所周知 hash 由于不能完全避免冲突常常会被 cf hacker 卡掉,此题有更稳定的解法 考虑将原串进行简化,由于只有连续的 1 会造成不确定的匹配,可以只对 0 建立一个新串 对于一段连续的 1,后面的 0 建立新字符,如果有奇数个 1 就建立 1,偶数个就建立 0 例如: 1110 -> 1,110 -> 0,11011 -> 0 由于最后的 1 是没有后导 0 的就不计入新串 然后新串的 lcp 就可以对应到原串的对应位置,新串 lcp 匹配就等价于原串

codeforces 427D Match &amp; Catch(后缀数组,字符串)

题目 参考:http://blog.csdn.net/xiefubao/article/details/24934617 题意:给两个字符串,求一个最短的子串.使得这个子串在两个字符串中出现的次数都等于1.出现的定义为:可以重叠的出现. 解法:后缀数组的应用.从小枚举长度.如果一个长度len合法的话:则一定存在这个样的sa[i]排名.sa[i]与s[i+1]的公共前缀长度大于等于len,且sa[i]与[i-1]的公共前缀长度小于len,同时sa[i+1]与[i+2]的公共前缀长度小于len,同时

CF 504E Misha and LCP on Tree(树链剖分+后缀数组)

题目链接:http://codeforces.com/problemset/problem/504/E 题意:给出一棵树,每个结点上有一个字母.每个询问给出两个路径,问这两个路径的串的最长公共前缀. 思路:树链剖分,记录每条链的串,正反都记,组成一个大串.记录每条链对应的串在大串中的位置.然后对大串求后缀数组.最后询问就是在一些链上的查询. 树链剖分总是那么优秀.. const int N=600005; int next[N],node[N],head[N],e; void add(int u

CF(427D-Match &amp;amp; Catch)后缀数组应用

题意:给两个字符串,求一个最短的子串.使得这个子串在两个字符串中出现的次数都等于1.出现的定义为:能够重叠的出现. 解法:后缀数组的应用.从小枚举长度.假设一个长度len合法的话:则一定存在这个样的sa[i]排名.sa[i]与s[i+1]的公共前缀长度大于等于len,且sa[i]与[i-1]的公共前缀长度小于len,同一时候sa[i+1]与[i+2]的公共前缀长度小于len,同一时候保证sa[i]与sa[i+1]在两个串中.Judge函数就是技巧性地实现了这些推断. 代码: #include<i

CF 427D Match &amp; Catch 求最短唯一连续LCS

题目来源:CF 427D Match & Catch 题意:给出2个字符串 求最短的连续的公共字符串 并且该字符串在原串中只出现一次 思路:把2个字符串合并起来求height 后缀数组height的应用 #include <cstdio> #include <cstring> #include <algorithm> using namespace std; const int maxn = 100010; char s[maxn]; int sa[maxn];

SPOJ 705 Distinct Substrings(后缀数组)

[题目链接] http://www.spoj.com/problems/SUBST1/ [题目大意] 给出一个串,求出不相同的子串的个数. [题解] 对原串做一遍后缀数组,按照后缀的名次进行遍历, 每个后缀对答案的贡献为n-sa[i]+1-h[i], 因为排名相邻的后缀一定是公共前缀最长的, 那么就可以有效地通过LCP去除重复计算的子串. [代码] #include <cstdio> #include <cstring> #include <algorithm> usi

hdu5769--Substring(后缀数组)

题意:求含有某个字母的某个字符串的不同子串的个数 题解:后缀数组,记录每个位置距离需要出现的字母的距离就可以了.因为不太了解后缀模版卡了一会,还是很简单的. 记住sa和height数组都是1-n的下标. //后缀数组 #include <stdio.h> #include <cstring> #include <iostream> #include <algorithm> using namespace std; typedef long long ll;