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 orchidSample Output
2 2
题目大意:给定n个字符串,求出现或反转后出现在每个字符串中的最长子串。
解题思路:根据<<后缀数组——处理字符串的有力工具>>的思路,这题不同的地方在于要判断是否在反转后的字符串中出现 。其实这并没有加大题目的难度 。 只需要先将每个字符串都反过来写一遍, 中间用一个互不相同的且没有出现在字符串中的字符隔开,再将 n个字符串全部连起来, 中间也是用一 个互不相同的且没有出现在字符串中的字符隔开, 求后缀数组 。 然后二分答案ans, 再将后缀分组,每组后缀的值都不小于ans。 判断的时候, 要看是否有一组后缀在每个原来的字符串或反转后的字符串中出现。这个做法的时间复杂度为 0(nlogn) 。
/* @Author: Top_Spirit @Language: C++ */ //#include <bits/stdc++.h> #include <iostream> #include <cstdio> #include <cmath> #include <cstring> #include <set> using namespace std ; typedef unsigned long long ull ; typedef long long ll ; const int Maxn = 1e5 + 10 ; const int INF = 0x3f3f3f3f ; const double PI = acos(-1.0) ; const int seed = 133 ; int sa[Maxn] ;//SA数组,表示将S的n个后缀从小到大排序后把排好序的 //的后缀的开头位置顺次放入SA中 int t1[Maxn], t2[Maxn], c[Maxn] ;//求SA数组需要的中间变量,不需要赋值 int Rank[Maxn], height[Maxn] ; //待排序的字符串放在s数组中,从s[0]到s[n-1],长度为n,且最大值小于m, //除s[n-1]外的所有s[i]都大于0,r[n-1]=0 //函数结束以后结果放在sa数组中 void getSa(int s[], int n, int m){ int i, j, p, *x = t1, *y = t2 ; //第一轮基数排序,如果s的最大值很大,可改为快速排序 for(i = 0; i < m; i++) c[i] = 0 ; for(i = 0; i < n; i++) c[x[i] = s[i]]++ ; for(i = 1; i < m; i++) c[i] += c[i-1] ; for(i = n-1; i >= 0; i--) sa[--c[x[i]]] = i ; for(j = 1; j <= n; j <<= 1){ p = 0 ; //直接利用sa数组排序第二关键字 for(i = n-j; i < n; i++) y[p++] = i ;//后面的j个数第二关键字为空的最小 for(i = 0; i < n; i++) if(sa[i] >= j) y[p++] = sa[i] - j ; //这样数组y保存的就是按照第二关键字排序的结果 //基数排序第一关键字 for(i = 0; i < m; i++) c[i] = 0 ; for(i = 0; i < n ; i++) c[x[y[i]]]++ ; for(i = 1; i < m; i++) c[i] += c[i-1] ; for(i = n-1; i >= 0; i--) sa[--c[x[y[i]]]] = y[i] ; //根据sa和x数组计算新的x数组 swap(x,y) ; p=1; x[sa[0]] = 0; for(i = 1; i < n; i++) x[sa[i]] = y[sa[i - 1]] == y[sa[i]] && y[sa[i - 1] + j] == y[sa[i] + j] ? p-1 : p++ ; if(p >= n) break ; m = p ;//下次基数排序的最大值 } } void getHeight(int s[],int n){ int i, j, k = 0 ; for(i = 1; i <= n; i++) Rank[sa[i]] = i ; for(i = 0; i < n; i++){ if(k) k-- ; j = sa[Rank[i] - 1] ; while(s[i + k] == s[j + k]) k++ ; height[Rank[i]] = k ; } } char str[Maxn]; int s[Maxn], n , index[Maxn] ; int len, asc ; bool check(int mid){ set < int > se ; for (int i = 1; i < len; i++){ if (height[i] >= mid){ se.insert(index[sa[i]]) ; se.insert(index[sa[i - 1]]) ; } else { if (se.size() == n) return true ; se.clear() ; } } if (se.size() == n) return true ; return false ; } void solve (){ if (n == 1) { cout << strlen(str) << endl ; return ; } int l = 1, r = 100, ans =0 ; while (l <= r){ int mid = l + r >> 1 ; if (check(mid)) { ans = mid ; l = mid + 1 ; } else r = mid - 1 ; } cout << ans << endl ; return ; } int main(){ int T ; cin >> T ; while (T--){ cin >> n ; len = 0 ; asc = 0 ; for (int i = 1; i <= n; i++){ cin >> str ; int lens = strlen(str) ; for (int j = 0; j < lens; j++){ index[len] = i ; s[len++] = str[j] - 'A' + 2 * n + 1 ; } index[len] = i ; s[len++] = asc++ ; for (int j = lens - 1; j >= 0; j--){ index[len] = i ; s[len++] = str[j] - 'A' + 2 * n + 1 ; } index[len] = i ; s[len++] = asc++; } getSa(s, len, 520) ; getHeight(s, len - 1) ; solve () ; } return 0; }