poj 3518 Corporate Identity 后缀数组->多字符串最长相同连续子串

题目链接

题意:输入N(2 <= N <= 4000)个长度不超过200的字符串,输出字典序最小的最长公共连续子串;

思路:将所有的字符串中间加上分隔符,注:分隔符只需要和输入的字符不同,且各自不同即可,没有必要是最小的字符;

连接后缀数组求解出height之后二分长度,由于height是根据sa数组建立的,所以前面符合的就是字典序最小的,直接找到就停止即可;

ps: 把之前的模板简化了下,A题才是关键;

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string.h>
#include<algorithm>
#include<map>
#include<queue>
#include<vector>
#include<cmath>
#include<stdlib.h>
#include<time.h>
using namespace std;
#define MS0(a) memset(a,0,sizeof(a))
typedef long long ll;
const int MAXN = 1000005;
int sa[MAXN],t[MAXN],t2[MAXN],c[MAXN],wv[MAXN];
int cmp(int *r, int a, int b, int l){
    return r[a] == r[b] && r[a+l] == r[b+l];
}
void build_sa(int *r, int n, int m){          //  倍增算法 r为待匹配数组  n为总长度 m为字符范围
    int i, j, p, *x = t, *y = t2;
    for(i = 0; i < m; i ++) c[i] = 0;
    for(i = 0; i < n; i ++) c[x[i] = r[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, p = 1; p < n; j <<= 1, m = p){
        for(p = 0, i = n-j; i < n; i ++) y[p++] = i;
        for(i = 0; i < n; i ++) if(sa[i] >= j) y[p++] = sa[i] - j;
        for(i = 0; i < n; i ++) wv[i] = x[y[i]];
        for(i = 0; i < m; i ++) c[i] = 0;
        for(i = 0; i < n; i ++) c[wv[i]] ++;
        for(i = 1; i < m; i ++) c[i] += c[i-1];
        for(i = n-1; i >= 0; i --) sa[--c[wv[i]]] = y[i];
        for(swap(x,y), p = 1, x[sa[0]] = 0, i = 1; i < n; i++){
            x[sa[i]] = cmp(y, sa[i-1], sa[i], j) ? p - 1: p++;
        }
    }
}
int rk[MAXN],height[MAXN];
void getHeight(int *r,int n)
{
    for(int i = 1;i <= n;i++) rk[sa[i]] = i; // rk[i]:后缀i在sa[]中的下标
    for(int i = 0,j,k = 0; i < n; height[rk[i++]] = k){
        for(k? k--:0 ,j = sa[rk[i] - 1];r[i+k] == r[j+k];k++);
    }
}
int d[MAXN],num[MAXN],vs[4444],T,len,tot;
char tt[222];
bool check(int L)
{
    MS0(vs);
    int cnt = 0;
    for(int i = 2;i <= tot;i++){
        if(height[i] < L){
            MS0(vs);
            cnt = 0;
            continue;
        }
        if(!vs[d[sa[i]]]){
            vs[d[sa[i]]] = 1;cnt++;
        }
        if(!vs[d[sa[i-1]]]){
            vs[d[sa[i-1]]] = 1;cnt++;
        }
        if(cnt == T){ //对于同一个长度,我们只取第一次出现的符合条件的字符串;
            for(int j = 0; j<L; j++)
                    tt[j] = num[sa[i]+j];
                tt[L] = ‘\0‘;
            return true;
        }
    }
    return false;
}
char str[222];
int main()
{
    while(scanf("%d",&T) == 1 && T){
        tot = 0;
        for(int i = 1;i <= T;i++){
            scanf("%s",str);
            len = strlen(str);
            for(int j = 0;j < len;j++)
                num[tot] = str[j],d[tot++] = i;
            num[tot] = ‘z‘+i,d[tot++] = ‘z‘+i;// ‘#‘ + i竟然WA了
        }
        num[tot] = 0;//最后添加一个最小字符;
        build_sa(num,tot+1,5155);
        getHeight(num,tot);
        int ans = 0,l = 1,r = len,mid,id;
        while(l <= r){
            mid = l + r >> 1;
            if(check(mid)){ans = mid,l = mid + 1;}
            else r = mid - 1;
        }
        if(ans){
            printf("%s\n",tt);
        }
        else puts("IDENTITY LOST");
    }
    return 0;
}
时间: 2024-07-31 19:33:23

poj 3518 Corporate Identity 后缀数组->多字符串最长相同连续子串的相关文章

后缀数组求解字符串的最长重复子串

后缀数组 给定一个字符串,求出其最长的重复子串. 思路:使用后缀数组,对一个字符串生成相应的后缀数组后,然后再排序,排完序依次检测相邻的两个字符串的开头公共部分.这样的时间复杂度为: 生成后缀数组 O(N)排序 O(NlogN*N) 最后面的 N 是因为字符串比较也是 O(N)依次检测相邻的两个字符串 O(N * N)总的时间复杂度是 O(N^2*logN),

POJ - 1743 Musical Theme (后缀数组求不可重叠最长重复子串)

Description A musical melody is represented as a sequence of N (1<=N<=20000)notes that are integers in the range 1..88, each representing a key on the piano. It is unfortunate but true that this representation of melodies ignores the notion of music

POJ 3450 Corporate Identity KMP题解

本题要求求一组字符串的最长公共子串,其实是灵活运用KMP快速求最长前缀. 注意肯爹的题意:要求按照字典顺序输出. 还有要提醒的就是:有人也是用KMP来解这道题,但是很多人都把KMP当成暴力法来用了,没有真正处理好细节,发挥KMP的作用.而通常这些人都大喊什么暴力法可以解决本题,没错,的确暴力法是可以解决本题的,本题的数据不大,但是请不要把KMP挂上去,然后写成暴力法了,那样会误导多少后来人啊. 建议可以主要参考我的getLongestPre这个函数,看看是如何计算最长前缀的. 怎么判断你是否把本

poj 3261 后缀数组 找重复出现k次的子串(子串可以重叠)

题目:http://poj.org/problem?id=3261 仍然是后缀数组的典型应用----后缀数组+lcp+二分 做的蛮顺的,1A 但是大部分时间是在调试代码,因为模板的全局变量用混了,而自己又忘了,,,等西安邀请赛还有四省赛结束之后,该冷静反思下尝试拜托模板了 错误   :1.k用错,题目的k和模板的k用混; 2.还是二分的C()函数,这个其实跟前一篇<poj 1226 hdu 1238 Substrings 求若干字符串正串及反串的最长公共子串 2002亚洲赛天津预选题>的C函数

POJ 3882 Stammering Aliens 后缀数组height应用

题目来源:POJ 3882 Stammering Aliens 题意:给你m一个一个字符串 求至少出现m次的最长字符串 可以在字符串中重叠出现 思路:二分长度l 然后从height数组中找长度大于等于l的前缀 #include <cstdio> #include <cstring> #include <algorithm> using namespace std; const int maxn = 40010; char s[maxn]; int sa[maxn]; i

POJ 1743 后缀数组:求最长不重叠子串

数据:这题弄了好久,WA了数十发,现在还有个例子没过,可却A了,POJ 的数组也太弱了. 10 1 1 1 1 1 1 1 1 1 1 这组数据如果没有那个n-1<10判断的话,输入的竟然是5,我靠-- 思路:这个题目关键的地方有两个:第一,重复的子串一定可以看作是某两个后缀的公共前缀,第二,把题目转化成去判定对于任意的一个长度k,是否存在长度至少为k的不重叠的重复的子串. 转化成判定问题之后,就可以二分去解答了.在验证判定是否正确时,我们可以把相邻的所有不小于k的height[]看成一组,然后

POJ 3294 Life Forms (后缀数组)

题目大意: 求出在m个串中出现过大于m/2次的子串. 思路分析: 如果你只是直接跑一次后缀数组,然后二分答案扫描的话. 那么就试一下下面这个数据. 2 abcdabcdefgh efgh 这个数据应该输出 efgh 问题就在于对于每一个串,都只能参与一次计数,所以在check的时候加一个标记数组是正解. #include <cstdio> #include <iostream> #include <algorithm> #include <cstring>

利用后缀数组(suffix array)求最长公共子串(longest common substring)

摘要:本文讨论了最长公共子串的的相关算法的时间复杂度,然后在后缀数组的基础上提出了一个时间复杂度为o(n^2*logn),空间复杂度为o(n)的算法.该算法虽然不及动态规划和后缀树算法的复杂度低,但其重要的优势在于可以编码简单,代码易于理解,适合快速实现. 首先,来说明一下,LCS通常指的是公共最长子序列(Longest Common Subsequence,名称来源参见<算法导论>原书第3版p223),而不是公共最长子串(也称为最长公共子串). 最长公共子串问题是在文本串.模式串中寻找共有的

URAL 1297 后缀数组:求最长回文子串

思路:这题下午搞了然后一直WA,后面就看了Discuss,里面有个数组:ABCDEFDCBA,这个我输出ABCD,所以错了. 然后才知道自己写的后缀数组对这个回文子串有bug,然后就不知道怎么改了. 然后看题解,里面都是用RMQ先预处理任意两个后缀的最长公共前缀,因为不太知道这个,所以又看了一下午,嘛嘛-- 然后理解RMQ和后缀一起用的时候才发现其实这里不用RMQ也可以,只要特殊处理一下上面这个没过的例子就行了,哈哈--机智-- 不过那个国家集训队论文里面正解是用RMQ做的,自己还得会和RMQ一