POJ1226-Substrings

Substrings
Time Limit: 1000MS Memory Limit: 10000K
Total Submissions: 14668 Accepted: 5172

Description

You are given a number of case-sensitive strings of alphabetic characters, find the largest string X, such that either X, or its inverse can be found as a substring of any of the given strings.

Input

The first line of the input contains a single integer t (1 <= t <= 10), the number of test cases, followed by the input data for each test case. The first line of each test case contains a single integer n (1 <= n <= 100), the number of given strings, followed by n lines, each representing one string of minimum length 1 and maximum length 100. There is no extra white space before and after a string.

Output

There should be one line per test case containing the length of the largest string found.

Sample Input

2
3
ABCD
BCDFF
BRCD
2
rose
orchid

Sample Output

2
2 

Source



题意:给定n个字符串,求出现或反转后出现在每个字符串中的最长子串 

思路:先将每个字符串的原始串和翻转串连接起来,中间用一个互不相同的且没有出现在字符串中的字符隔开,再将n个字符串全部连起来,中间也是用一 个互不相同的且没有出现在字符串中的字符隔开,求后缀数组。然后二分答案, 再将后缀分组。判断时看是否有一组后缀在每个原来的字符串或反转后的字符串中出现。


#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <algorithm>
#include <map>
#include <set>
#include <stack>
#include <queue>
#include <vector>
#include <bitset>
#include <functional>

using namespace std;

#define LL long long
const int INF = 0x3f3f3f3f;
const int N = 200010;

int k;

struct Sa
{
	char ch[N];
	int s[N], vis[15];
	int rk[2][N], sa[N], h[N], w[N], now, n;
	int rmq[N][20], lg[N], bel[N];

	void GetS()
	{
		n = 0;
		scanf("%d", &k);
		int q = 'z' + 1;
		for (int i = 1; i <= k; i++)
		{
			scanf("%s", ch);
			int len = strlen(ch);
			for (int j = 0; j < len; j++) s[++n] = ch[j], bel[n] = i;
			s[++n] = q++;
			for (int j = len - 1; j >= 0; j--) s[++n] = ch[j], bel[n] = i;
			s[++n] = q++;
		}
	}

	void getsa(int z, int &m)
	{
		int x = now, y = now ^= 1;
		for (int i = 1; i <= z; i++) rk[y][i] = n - i + 1;
		for (int i = 1, j = z; i <= n; i++)
			if (sa[i] > z) rk[y][++j] = sa[i] - z;
		for (int i = 1; i <= m; i++) w[i] = 0;
		for (int i = 1; i <= n; i++) w[rk[x][rk[y][i]]]++;
		for (int i = 1; i <= m; i++) w[i] += w[i - 1];
		for (int i = n; i >= 1; i--) sa[w[rk[x][rk[y][i]]]--] = rk[y][i];
		for (int i = m = 1; i <= n; i++)
		{
			int *a = rk[x] + sa[i], *b = rk[x] + sa[i - 1];
			rk[y][sa[i]] = *a == *b&&*(a + z) == *(b + z) ? m - 1 : m++;
		}
	}

	void getsa(int m)
	{
		now = rk[1][0] = sa[0] = s[0] = 0;
		for (int i = 1; i <= m; i++) w[i] = 0;
		for (int i = 1; i <= n; i++) w[s[i]]++;
		for (int i = 1; i <= m; i++) rk[1][i] = rk[1][i - 1] + (bool)w[i];
		for (int i = 1; i <= m; i++) w[i] += w[i - 1];
		for (int i = 1; i <= n; i++) rk[0][i] = rk[1][s[i]];
		for (int i = 1; i <= n; i++) sa[w[s[i]]--] = i;
		rk[1][n + 1] = rk[0][n + 1] = 0;
		for (int x = 1, y = rk[1][m]; x <= n&&y <= n; x <<= 1) getsa(x, y);
		for (int i = 1, j = 0; i <= n; h[rk[now][i++]] = j ? j-- : j)
		{
			if (rk[now][i] == 1) continue;
			int k = n - max(sa[rk[now][i] - 1], i);
			while (j <= k&&s[sa[rk[now][i] - 1] + j] == s[i + j]) ++j;
		}
	}

	void getrmq()
	{
		h[n + 1] = h[1] = lg[1] = 0;
		for (int i = 2; i <= n; i++)
			rmq[i][0] = h[i], lg[i] = lg[i >> 1] + 1;
		for (int i = 1; (1 << i) <= n; i++)
		{
			for (int j = 2; j <= n; j++)
			{
				if (j + (1 << i) > n + 1) break;
				rmq[j][i] = min(rmq[j][i - 1], rmq[j + (1 << i - 1)][i - 1]);
			}
		}
	}

	int lcp(int x, int y)
	{
		int l = min(rk[now][x], rk[now][y]) + 1, r = max(rk[now][x], rk[now][y]);
		return min(rmq[l][lg[r - l + 1]], rmq[r - (1 << lg[r - l + 1]) + 1][lg[r - l + 1]]);
	}

	bool check(int x)
	{
		memset(vis, 0, sizeof vis);
		for (int i = 1; i <= n; i++)
		{
			if (h[i] >= x) vis[bel[sa[i]]]++, vis[bel[sa[i - 1]]]++;
			else
			{
				int flag = 1;
				for (int i = 1; i <= k; i++)
					if (!vis[i]) { flag = 0; break; }
				if (flag) return 1;
				memset(vis, 0, sizeof vis);
			}
		}
		int flag = 1;
		for (int i = 1; i <= k; i++)
			if (!vis[i]) { flag = 0; break; }
		return flag;
	}

	void work()
	{
		getsa(300);
		int l = 0, r = n, ans = 0;
		while (l <= r)
		{
			int mid = (l + r) >> 1;
			if (check(mid)) l = mid + 1, ans = mid;
			else r = mid - 1;
		}
		printf("%d\n", ans);
	}
}sa;

int main()
{
	int t;
	scanf("%d", &t);
	while (t--)
	{
		sa.GetS();
		sa.work();
	}
	return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值