您的位置:首页 > 其它

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++

#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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: