POJ 3450 后缀数组/KMP
题目链接: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的更多相关文章
- POJ 3080 后缀数组/KMP
题目链接:http://poj.org/problem?id=3080 题意:给定n个DNA串,求最长公共子串.如果最长公共子串的长度小于3时输出no significant commonalitie ...
- hdu5442(2015长春赛区网络赛1006)后缀数组+KMP /最小表示法?
题意:给定一个由小写字母组成的长度为 n 的字符串,首尾相连,可以从任意一个字符开始,顺时针或逆时针取这个串(长度为 n),求一个字典序最大的字符串的开始字符位置和顺时针或逆时针.如果有多个字典序最大 ...
- poj 3693 后缀数组 重复次数最多的连续重复子串
Maximum repetition substring Time Limit: 1000MS Memory Limit: 65536K Total Submissions: 8669 Acc ...
- POJ - 3294~Relevant Phrases of Annihilation SPOJ - PHRASES~Substrings POJ - 1226~POJ - 3450 ~ POJ - 3080 (后缀数组求解多个串的公共字串问题)
多个字符串的相关问题 这类问题的一个常用做法是,先将所有的字符串连接起来, 然后求后缀数组 和 height 数组,再利用 height 数组进行求解. 这中间可能需要二分答案. POJ - 3294 ...
- poj 2406 Power Strings (后缀数组 || KMP)
Power Strings Time Limit: 3000MS Memory Limit: 65536K Total Submissions: 28859 Accepted: 12045 D ...
- POJ 1226 后缀数组
题目链接:http://poj.org/problem?id=1226 题意:给定n个字符串[只含大小写字母],求一个字符串要求在n个串或者他们翻转后的串的出现过.输出满足要求的字符串的长度 思路:根 ...
- POJ - 2406 ~SPOJ - REPEATS~POJ - 3693 后缀数组求解重复字串问题
POJ - 2406 题意: 给出一个字符串,要把它写成(x)n的形式,问n的最大值. 这题是求整个串的重复次数,不是重复最多次数的字串 这题很容易想到用KMP求最小循环节就没了,但是后缀数组也能写 ...
- POJ 3415 后缀数组
题目链接:http://poj.org/problem?id=3415 题意:给定2个串[A串和B串],求两个串公共子串长度大于等于k的个数. 思路:首先是两个字符串的问题.所以想用一个'#'把两个字 ...
- UVA 11475 后缀数组/KMP
题目链接: 题意:给定一个只含字母的字符串,求在字符串末尾添加尽量少的字符使得字符串为回文串. 思路:因为只能从末尾添加字符,所以其实求的是最长的后缀回文串.那么添加的字符为除了这个原串的最长后缀回文 ...
随机推荐
- 【leetcode】Integer to Roman & Roman to Integer(easy)
Roman to Integer Given a roman numeral, convert it to an integer. Input is guaranteed to be within t ...
- 【python】classmethod & staticmethod 区别
来源:http://blog.csdn.net/carolzhang8406/article/details/6856817 其他参考: http://blog.csdn.net/lovingprin ...
- 20145213《Java程序设计》第二周学习总结
20145213<Java程序设计>第二周学习总结 教材学习内容总结 本周娄老师给的任务是学习教材的第三章--基础语法.其实我觉得还蛮轻松的,因为在翻开厚重的书本,一股熟悉的气息扑面而来, ...
- 用css解决iframe的自适应问题(跨域下同样有用)
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xht ...
- 使用Ajax上传图片到服务器(不刷新页面)
有时候我们需要上传图片时不刷新页面,那么Ajax就是很好的东西哦.之前在网上找了很多的资料都不对,不是这里就是那里错,这是本人亲自测试了的哈,是没有问题的,若有不足之处希望指正.我用的.net,对了这 ...
- yii和php的一些细节
yii index.php ini_set("display_errors", 1); iconv这个函数不能直接用 需要检测!!! function gb2312_utf8( ...
- UVa1593_Allgnment_Of_Code
/** start: integer; // begins hear stop: integer; // ends here s: string; c: char; // temp **/ //测试数 ...
- hdu1798(几何面积计算)
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1798 题意:给出两个圆的圆心坐标与半径,求他们相交部分的大小 思路:有三种情况: 1. 两圆相离,ar ...
- 二、JavaScript语言--JS基础--JavaScript进阶篇--DOM对象 控制HTML元素
1.认识DOM 文档对象模型DOM(Document Object Model)定义访问和处理HTML文档的标准方法.DOM 将HTML文档呈现为带有元素.属性和文本的树结构(节点树). 先来看看下面 ...
- WPF框架MVVM简单例子
MVVM是Model-View-ViewModel的缩写形式,它通常被用于WPF或Silverlight开发.Model——可以理解为带有字段,属性的类.View——可以理解为我们所看到的UI.Vie ...