L - Substrings POJ - 1226 (后缀数组+二分)

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 

题目大意:给定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;
}

 

1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看REAdMe.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看REAdMe.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看READme.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值