题目链接:http://poj.org/problem?id=3450

题意:给定n个字符串,求n个字符串的最长公共子串,无解输出IDENTITY LOST,否则最长的公共子串。有多组解时输出字典序最小的解

思路:后缀数组的解法,我们把n个串都链接起来,中间用一些互不相同的且都没在原串中出现过的字符来分割开。然后求后缀数组。由于求的是最长公共子串,所以我们可以二分长度x,于是问题就转变成了是否有一个长度为x的子串在n个字符串中都出现过。判断的方式是:以height数组进行分组,height值不小于x的为一组,如果有一组的后缀在原来n个串中都出现过。则说明存在长度为x的子串满足要求。由于答案要求输出字典序最小值的串,所以第一组满足要求的一定是字典序最小的解。因为sa数组的定义就是所有后缀按字典序排序。因此只需要找到第一组就可以返回了。

坑点:由于数据范围很大4000*200。所以在分组判断的时候不能情况所以的标记否则TLE到死。

#define _CRT_SECURE_NO_DEPRECATE
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<string>
#include<queue>
#include<vector>
#include<time.h>
#include<cmath>
#include<set>
using namespace std;
typedef long long int LL;
const int MAXN = + ;
int wa[MAXN], wb[MAXN], wv[MAXN], WS[MAXN];
int cmp(int *r, int a, int b, int l)
{
return r[a] == r[b] && r[a + l] == r[b + l];
}
void da(int *r, int *sa, int n, int m)
{
int i, j, p, *x = wa, *y = wb, *t;
for (i = ; i < m; i++) WS[i] = ;
for (i = ; i < n; i++) WS[x[i] = r[i]]++;
for (i = ; i < m; i++) WS[i] += WS[i - ];
for (i = n - ; i >= ; i--) sa[--WS[x[i]]] = i;
for (j = , p = ; p < n; j *= , m = p)
{
for (p = , i = n - j; i < n; i++) y[p++] = i;
for (i = ; i < n; i++) if (sa[i] >= j) y[p++] = sa[i] - j;
for (i = ; i < n; i++) wv[i] = x[y[i]];
for (i = ; i < m; i++) WS[i] = ;
for (i = ; i < n; i++) WS[wv[i]]++;
for (i = ; i < m; i++) WS[i] += WS[i - ];
for (i = n - ; i >= ; i--) sa[--WS[wv[i]]] = y[i];
for (t = x, x = y, y = t, p = , x[sa[]] = , i = ; i < n; i++)
x[sa[i]] = cmp(y, sa[i - ], sa[i], j) ? p - : p++;
}
return;
}
int Rank[MAXN], height[MAXN], sa[MAXN];
void calheight(int *r, int *sa, int n){
int i, j, k = ;
for (i = ; i <= n; i++) { Rank[sa[i]] = i; }
for (i = ; i < n; height[Rank[i++]] = k){
for (k ? k-- : , j = sa[Rank[i] - ]; r[i + k] == r[j + k]; k++);
}
return;
}
int r[MAXN], len, n, Index[MAXN], pos[ + ], vis[ + ];
char str[ + ];
bool check(int x){
int cnt = ;
for (int i = ; i <= n; i++){
vis[i] = ;//坑点,如果用memset来清空所以标记会TLE
}
for (int i = ; i < len; i++){
if (height[i] >= x){
if (!vis[Index[sa[i]]]){
cnt++;vis[Index[sa[i]]] = ;
}
if (!vis[Index[sa[i - ]]]){
cnt++;vis[Index[sa[i - ]]] = ;
}
if (cnt == n){
pos[x] = sa[i];return true;
}
}
else{
for (int i = ; i <= n; i++){
vis[i] = ;//坑点,如果用memset来清空所以标记会TLE
}
cnt = ;
}
}
return false;
}
void solve(){
int L = , R = , mid, ans = ;
memset(pos, , sizeof(ans));
while (R >= L){
mid = (L + R) / ;
if (check(mid)){
ans = mid;
L = mid + ;
}
else{
R = mid - ;
}
}
if (ans == ){
printf("IDENTITY LOST\n");
}
else{
for (int i = pos[ans], j = ; j < ans; j++, i++){
printf("%c", (r[i] - n - ) + 'a');
}
printf("\n");
}
}
int main(){
//#ifdef kirito
// freopen("in.txt", "r", stdin);
// freopen("out.txt", "w", stdout);
//#endif
// int start = clock();
while (scanf("%d", &n) && n){
len = ;
for (int i = , val = ; i <= n; i++){
scanf("%s", &str);
for (int j = ; j < strlen(str); j++){
Index[len] = i; //属于哪个串
r[len++] = (str[j] - 'a' + n + ); //由于中间会添加n个分隔符。所以a字符映射成n+1
}
Index[len] = i;
r[len++] = val++;
}
da(r, sa, len, );
calheight(r, sa, len - );
solve();
}
//#ifdef LOCAL_TIME
// cout << "[Finished in " << clock() - start << " ms]" << endl;
//#endif
return ;
}

思路二:再来说说KMP的做法,由于求的是公共子串,所以答案[如果存在答案]肯定会在每个串中出现。子串一定是某个后缀的前缀,所以我们枚举随便一个串的所有后缀[这里我枚举的是第一个输入的串str[0]],然后我们对于每个后缀去匹配其他n-1个字符串,看看能匹配的最长前缀的长度是多少。比如现在有4个串,我们拿第一个串的某个后缀[后缀s]和其他3个串来匹配。s和str[1]匹配长度为3,说明后缀s的前3个字符在str[1]中连续出现过。s和str[2]匹配长度为2,说明后缀s的前2个字符在str[2]中连续出现过。s和str[3]匹配的长度是1,说明后缀s的前1个字符在str[3]中连续出现过。所以后缀s和str[1~3]的最长公共子串匹配的长度为1。然后考虑到字符串匹配的问题,所以对每个后缀都求一次next数组加速匹配即可。 在看到字典序的时候可以暴力判断,也可以求str[0]的名词数组rank来判断。而且本题KMP做法比后缀数组要快很多。

#define _CRT_SECURE_NO_DEPRECATE
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<string>
#include<queue>
#include<vector>
#include<time.h>
#include<cmath>
#include<set>
using namespace std;
typedef long long int LL;
const int MAXN = + ;
const int MAXL = + ;
char str[MAXN][MAXL], ans[MAXL];
int Next[MAXL], n;
void getNext(char *str, int *Next, int len){
memset(Next, , sizeof(Next));
int i = , j = -; Next[] = -;
while (i < len){
if (j == - || str[i] == str[j]){
i++; j++; Next[i] = j;
}
else{
j = Next[j];
}
}
}
int LongestPre(char *s, int len){
getNext(s, Next, len);
int lenpre = MAXL;
for (int i = ; i < n; i++){ //与其他N-1个串进行匹配
int tmp = , k = ;
for (int j = ; j < strlen(str[i]); j++){
while (k != - && s[k] != str[i][j]){
k = Next[k];
}
if (k != - && s[k] == str[i][j]){
k++; tmp = max(tmp, k);
}
if (k == len){ break; }
if (k == -){ k = ; }
}
lenpre = min(lenpre, tmp); //匹配长度为所以长度的最小值
}
return lenpre;
}
int main(){
//#ifdef kirito
// freopen("in.txt", "r", stdin);
// freopen("out.txt", "w", stdout);
//#endif
// int start = clock();
while (scanf("%d", &n) && n){
for (int i = ; i < n; i++){
scanf("%s", &str[i]);
}
int maxpre = , idx = , len = strlen(str[]);
for (int i = ; i < len; i++){//枚举str[0]的所以后缀
int lenpre = LongestPre(str[] + i, len - i);//后缀的最长前缀匹配长度
if (lenpre >= maxpre){
if (maxpre < lenpre){
maxpre = lenpre; idx = i;
}
else if (maxpre == lenpre){ //相同解,暴力判断字典序
for (int j = ; j < maxpre; j++){
if (str[][idx + j] > str[][i + j]){
idx = i; break;
}
if (str[][idx + j] < str[][i + j]){
break;
}
}
}
}
}
if (maxpre == ){
printf("IDENTITY LOST\n");
}
else{
for (int i = ; i < maxpre; i++){
printf("%c", str[][idx + i]);
}
printf("\n");
}
}
//#ifdef LOCAL_TIME
// cout << "[Finished in " << clock() - start << " ms]" << endl;
//#endif
return ;
}

POJ 3450 后缀数组/KMP的更多相关文章

  1. POJ 3080 后缀数组/KMP

    题目链接:http://poj.org/problem?id=3080 题意:给定n个DNA串,求最长公共子串.如果最长公共子串的长度小于3时输出no significant commonalitie ...

  2. hdu5442(2015长春赛区网络赛1006)后缀数组+KMP /最小表示法?

    题意:给定一个由小写字母组成的长度为 n 的字符串,首尾相连,可以从任意一个字符开始,顺时针或逆时针取这个串(长度为 n),求一个字典序最大的字符串的开始字符位置和顺时针或逆时针.如果有多个字典序最大 ...

  3. poj 3693 后缀数组 重复次数最多的连续重复子串

    Maximum repetition substring Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 8669   Acc ...

  4. POJ - 3294~Relevant Phrases of Annihilation SPOJ - PHRASES~Substrings POJ - 1226~POJ - 3450 ~ POJ - 3080 (后缀数组求解多个串的公共字串问题)

    多个字符串的相关问题 这类问题的一个常用做法是,先将所有的字符串连接起来, 然后求后缀数组 和 height 数组,再利用 height 数组进行求解. 这中间可能需要二分答案. POJ - 3294 ...

  5. poj 2406 Power Strings (后缀数组 || KMP)

    Power Strings Time Limit: 3000MS   Memory Limit: 65536K Total Submissions: 28859   Accepted: 12045 D ...

  6. POJ 1226 后缀数组

    题目链接:http://poj.org/problem?id=1226 题意:给定n个字符串[只含大小写字母],求一个字符串要求在n个串或者他们翻转后的串的出现过.输出满足要求的字符串的长度 思路:根 ...

  7. POJ - 2406 ~SPOJ - REPEATS~POJ - 3693 后缀数组求解重复字串问题

    POJ - 2406 题意: 给出一个字符串,要把它写成(x)n的形式,问n的最大值. 这题是求整个串的重复次数,不是重复最多次数的字串 这题很容易想到用KMP求最小循环节就没了,但是后缀数组也能写 ...

  8. POJ 3415 后缀数组

    题目链接:http://poj.org/problem?id=3415 题意:给定2个串[A串和B串],求两个串公共子串长度大于等于k的个数. 思路:首先是两个字符串的问题.所以想用一个'#'把两个字 ...

  9. UVA 11475 后缀数组/KMP

    题目链接: 题意:给定一个只含字母的字符串,求在字符串末尾添加尽量少的字符使得字符串为回文串. 思路:因为只能从末尾添加字符,所以其实求的是最长的后缀回文串.那么添加的字符为除了这个原串的最长后缀回文 ...

随机推荐

  1. 【leetcode】Next Permutation(middle)

    Implement next permutation, which rearranges numbers into the lexicographically next greater permuta ...

  2. odoo注销后在登录时的用户名和密码

    初识odoo时会遇到注销后无法登陆的情况,一般原因是没有留意管理员邮件地址和对应的密码所致.初始情况下默认的邮件地址为admin,密码为数据库创建时提供的密码.

  3. a标签的妙用-拨打电话、发送短信、发送邮件

    前端时间在做手机WAP网站时,遇到需要点击页面上显示的电话号能直接拨号的需求,查找资料发现可以使用html的a标签完美实现该需求!记录下来以备后用...... 目前主流手机浏览器对H5的支持已经很不错 ...

  4. September 2nd 2016 Week 36th Friday

    How does the world look through your eyes? 你眼里的世界是什么样子的? How does the world look through your eyes? ...

  5. July 3rd, Week 28th Sunday, 2016

    2016-07-03 First-hand experience is often necessary for personal growth. 亲身体验通常是成长所必须的. Although som ...

  6. 二、获取微信用户openId

    /// <summary> /// 登录首页 /// </summary> /// <returns></returns> public ActionR ...

  7. iOS - 富文本AttributedString

    最近项目中用到了图文混排,所以就研究了一下iOS中的富文本,打算把研究的结果分享一下,也是对自己学习的一个总结. 在iOS中或者Mac OS X中怎样才能将一个字符串绘制到屏幕上呢?         ...

  8. cascade 介绍与用法 ( oracle)

    级联删除,比如你删除某个表的时候后面加这个关键字,会在删除这个表的同时删除和该表有关系的其他对象 1.级联删除表中的信息,当表A中的字段引用了表B中的字段时,一旦删除B中该字段的信息,表A的信息也自动 ...

  9. jQuery – 3.JQuery的Dom操作

    3.1 JQuery的Dom操作     1.使用html()方法读取或者设置元素的innerHTML    2.使用text()方法读取或者设置元素的innerText     3.使用attr() ...

  10. Deci and Centi Seconds parsing in java

    http://stackoverflow.com/questions/14558663/deci-and-centi-seconds-parsing-in-java