Codeforces Round #244 (Div. 2)——Match & Catch

题目链接

  • 题意:
    给定两个字符串,长度均不超过5000。求两个串的最短相同字串长度,且这个字串在两个串中均只出现一次
  • 分析:
    字符串的长度显然可以允许O(n*n)的算法,所以可以解决的方法很多。。。
1.DP
  • 分析:
    既然允许n方的算法,那么就可以存下A、B两个串的所有后缀的最长公共前缀。想象一下,对于A中的i位置和B中的j位置,假如此时两个位置的LCP(最长公共前缀)的长度为x,那么接下来我们要看是否在两个串中只出现了一次:对于A串,我们需要知道i位置和所有其他位置的LCP,记为,结果肯定大于这个值(小于会有重复),B串同理。

d1[i][j]表示串Ai位和j位的LCP,d2[i][j]表示B的,dp[i][j]表示A的i位和B的j位的LCP,m1[i]表示A的i位的不重复最短串,m2[i]表示B的

const int MAXN = 5100;

char a[MAXN], b[MAXN];
int dp[MAXN][MAXN];
int d1[MAXN][MAXN], d2[MAXN][MAXN];
int m1[MAXN], m2[MAXN];

int main()
{
//    freopen("in.txt", "r", stdin);
	RS(a); RS(b);
	int l1 = strlen(a), l2 = strlen(b);
	FED(i, l1 - 1, 0) FF(j, i + 1, l1)
	{
		if (a[i] != a[j]) d1[i][j] = 0;
		else d1[i][j] = d1[i + 1][j + 1] + 1;
		d1[j][i] = d1[i][j];
	}
	FED(i, l2 - 1, 0) FF(j, i + 1, l2)
	{
		if (b[i] != b[j]) d2[i][j] = 0;
		else d2[i][j] = d2[i + 1][j + 1] + 1;
		d2[j][i] = d2[i][j];
	}
	FED(i, l1 - 1, 0) FED(j, l2 - 1, 0)
	{
		if (a[i] != b[j]) dp[i][j] = 0;
		else dp[i][j] = dp[i + 1][j + 1] + 1;
	}
	REP(i, l1) REP(j, l1) m1[i] = max(m1[i], d1[i][j]);
	REP(i, l1) m1[i]++;
	REP(i, l2) REP(j, l2) m2[i] = max(m2[i], d2[i][j]);
	REP(i, l2) m2[i]++;
	int ans = INF;
	REP(i, l1)
		REP(j, l2)
			if (dp[i][j] >= m1[i] && dp[i][j] >= m2[j])
				ans = min(ans, max(m1[i], m2[j]));
	if (ans == INF) WI(-1);
	else WI(ans);
	return 0;
}


2.字符串Hash

3.后缀数组

  • 分析:
    对于后缀数组的解法,先考虑朴素的:在height数组中,先找到两个分别属于A、B串的位置i、j,设LCP为x,之后要考虑在A、B中没有重复出现,也就是要找到i和所有的A的LCP的最大值a,j和所有的B的最大值b,然后如果x大于a且大于b,那么取a和b的最大值然后加一即可。这样处理是O(n*n),对于这个题已经可以解

    然后考虑,如果i和j是分开的,那么显然考虑i和j相邻是更优的,所以之后就只考虑i和i+1。不妨设i - 1属于A,i属于B,最大值这个限制其实可以这样想,求两个串的LCP是求区间最小值,那么这个值肯定不大于第一个值(在i - 1上边的第一个height值)。在i上边,如果要在此位置有解,那么height[i - 1]一定小于height[i],因为如果上一个串是A,那么必然小于当前height(因为i - 1属于A),且再上边的一定满足;如果上一个是B,那么也一样(因为i属于B),且再上边的也一样满足。这样就是充要条件了。所以,如果i - 1和i属于不同的串,那么只需要比上边和下边第一个的height值大即可,结果是两个height的最大值加一

const int MAXN = 11000;

struct SuffixArray {
	int s[MAXN];      // 原始字符数组(最后一个字符应必须是0,而前面的字符必须非0)
	int sa[MAXN];     // 后缀数组
	int rank[MAXN];   // 名次数组. sa[0]一定是n-1,即最后一个字符
	int height[MAXN]; // height数组
	int t[MAXN], t2[MAXN], c[MAXN]; // 辅助数组
	int n; // 字符个数

	void clear() {
		n = 0; CLR(sa, 0); }

	// m为最大字符值加1。调用之前需设置好s和n
	void build_sa(int m) {
		int *x = t, *y = t2;
		REP(i, m) c[i] = 0;
		REP(i, n) c[x[i] = s[i]]++;
		FF(i, 1, m) c[i] += c[i-1];
		FED(i, n - 1, 0) sa[--c[x[i]]] = i;
		for(int k = 1; k <= n; k <<= 1) {
			int p = 0;
			FF(i, n - k, n) y[p++] = i;
			REP(i, n) if(sa[i] >= k) y[p++] = sa[i]-k;
			REP(i, m) c[i] = 0;
			REP(i, n) c[x[y[i]]]++;
			REP(i, m) c[i] += c[i-1];
			FED(i, n - 1, 0) sa[--c[x[y[i]]]] = y[i];
			swap(x, y);
			p = 1; x[sa[0]] = 0;
			FF(i, 1, n)
				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 build_height() {
		int k = 0;
		REP(i, n) rank[sa[i]] = i;
		REP(i, n) {
			if(k) k--;
			int j = sa[rank[i]-1];
			while(s[i+k] == s[j+k]) k++;
			height[rank[i]] = k;
		}
	}
} sa;

char ipt[MAXN];
int main()
{
	//    freopen("in.txt", "r", stdin);
	int n, m = 256;
	sa.clear();

	RS(ipt);
	n = strlen(ipt);
	REP(i, strlen(ipt)) sa.s[sa.n++] = (int)ipt[i];
	sa.s[sa.n++] = m++;
	RS(ipt);
	REP(i, strlen(ipt)) sa.s[sa.n++] = (int)ipt[i];
	sa.s[sa.n++] = 0;

	sa.build_sa(m);
	sa.build_height();
	int ans = INF;
	FF(i, 1, sa.n)
	{
		if ((sa.sa[i - 1] < n) + (sa.sa[i] < n) == 1 && sa.height[i - 1] < sa.height[i] && sa.height[i] > sa.height[i + 1])
			ans = min(ans, max(sa.height[i - 1], sa.height[i + 1]) + 1);
	}
	if (ans != INF) WI(ans);
	else WI(-1);
	return 0;
}


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值