LCP poj 2217 寻找最长公共子串

2024-05-28 05:08

本文主要是介绍LCP poj 2217 寻找最长公共子串,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

题目:http://poj.org/problem?id=2217

首先解释,DP中的最长公共子序列和此处的最长公共子串区别-------------------序列可以是不连续的,但是子串是连续的

其次,LCP,lcp[i]就是lcp[rank[i]]和lcp[rank[i]+1]的最长公共前缀,那么把两个字符串接起来,然后找最长的lcp,就是答案


思路还是比较清晰的


上代码:

/*******************************************************/
//poj 2217 lcp+sa by Pilgrim
//最长公共子串---注意与动态规划的最长公共子序列不同
//2014.4.2
/******************************************************/#include <string>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <iostream>
#include <cstdlib>
#include <vector>#define MAXN 10010
#define INF 0x80000000
//0x7fffffff
//0x80000000using namespace std;int n,k;//n=strlen(s);int Rank[MAXN];
int tmp[MAXN];/*使用Rank对sa排序*/
bool cmpSa(int i, int j)
{if(Rank[i] != Rank[j])return Rank[i] < Rank[j];else{   /*下面的Rank[t],已经是以t开头长度小于等于k/2的,sa[i]的名次,只是以i开头的后缀,而长度不同*/int ri = i+k <=n? Rank[i+k]:-1;int rj = j+k <= n ? Rank[j+k]:-1;return ri <rj;}
}/*计算SA*/
void con_sa(char *s, int *sa)
{/*n=strlen(s);  必要时注明*//*初始化sa和rank保证两点1、Rank[i]表示下标为i的是第几大,必须表示出相对大小,可以直接用字符代表其大小2、sa[1...n]值为1..n*/for(int i=0;i<=n;i++){sa[i]=i;Rank[i] = i < n?s[i]:-1;}/*利用长度为k的字符串对长度为2*k的字符串排序*/for(k=1;k<=n;k*=2)/*注意此代码中k是全局变量 别乱用,循环必须从1开始,因为0*2=0*/{sort(sa,sa+n+1,cmpSa);tmp[sa[0]] = 0; /*此时tmp只是暂存rank*/for(int i=1;i<=n;i++){tmp[sa[i]] = tmp[sa[i-1]] +(cmpSa(sa[i-1],sa[i])?1:0);/*这一句很关键,等号右侧的sa[i]在此循环里表示第i大的长度小于等于k/2的字符串,从而求出第i大的长度小于等于k的字符串的sa[i]*/}for(int i=0;i<=n;i++){Rank[i] = tmp[i];}}
}void construct_lcp(char *s,int *sa,int *lcp)
{//n=strlen(s);for(int i=0; i<=n; i++)Rank[sa[i]]=i;int h=0;lcp[0]=0;for(int i=0;i<n;i++){int j=sa[Rank[i]-1];if(h>0)h--;for(; j+h<n && i+h<n; h++){if(s[j+h]!=s[i+h])break;}lcp[Rank[i]-1]=h;}
}int main()
{int sa[MAXN],lcp[MAXN];char s[MAXN],t[MAXN];char c;int ncase,mmax,len,leng;while(scanf("%d",&ncase)!=EOF){while(ncase--){mmax =0;while((c=getchar())==' '|| c=='\n');s[0]=c;int tt=1;while((c=getchar()) != '\n'){s[tt++]=c;}s[tt]='\0';while((c=getchar())==' '|| c=='\n');t[0]=c;tt=1;while((c=getchar()) != '\n'){t[tt++]=c;}t[tt]='\0';len=strlen(s);leng =len+1+strlen(t);strcpy(s+len+1,t);
///
//for(int i=0;i<leng;i++)//   putchar(s[i]);
//putchar('\n');n=leng;con_sa(s,sa);construct_lcp(s,sa,lcp);for(int i=0;i<leng;i++){if((sa[i]<len) != (sa[i+1]<len))mmax=max(mmax,lcp[i]);}printf("Nejdelsi spolecny retezec ma delku %d.\n",mmax);}}return 0;
}


这篇关于LCP poj 2217 寻找最长公共子串的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1009619

相关文章

poj2406(连续重复子串)

题意:判断串s是不是str^n,求str的最大长度。 解题思路:kmp可解,后缀数组的倍增算法超时。next[i]表示在第i位匹配失败后,自动跳转到next[i],所以1到next[n]这个串 等于 n-next[n]+1到n这个串。 代码如下; #include<iostream>#include<algorithm>#include<stdio.h>#include<math.

poj3261(可重复k次的最长子串)

题意:可重复k次的最长子串 解题思路:求所有区间[x,x+k-1]中的最小值的最大值。求sa时间复杂度Nlog(N),求最值时间复杂度N*N,但实际复杂度很低。题目数据也比较水,不然估计过不了。 代码入下: #include<iostream>#include<algorithm>#include<stdio.h>#include<math.h>#include<cstring

spoj705( 求不相同的子串个数)

题意:求串s的不同子串的个数 解题思路:任何子串都是某个后缀的前缀,对n个后缀排序,求某个后缀的前缀的个数,减去height[i](第i个后缀与第i-1 个后缀有相同的height[i]个前缀)。 代码如下: #include<iostream>#include<algorithm>#include<stdio.h>#include<math.h>#include<cstrin

poj1330(LCA最近公共祖先)

题意:求最近公共祖先 思路:之前学习了树链剖分,然后我就用树链剖分的一小部分知识就可以解这个题目了,记录每个结点的fa和depth。然后查找时,每次将depth大的结点往上走直到x = y。 代码如下: #include<iostream>#include<algorithm>#include<stdio.h>#include<math.h>#include<cstring>

poj 3974 and hdu 3068 最长回文串的O(n)解法(Manacher算法)

求一段字符串中的最长回文串。 因为数据量比较大,用原来的O(n^2)会爆。 小白上的O(n^2)解法代码:TLE啦~ #include<stdio.h>#include<string.h>const int Maxn = 1000000;char s[Maxn];int main(){char e[] = {"END"};while(scanf("%s", s) != EO

hdu 2602 and poj 3624(01背包)

01背包的模板题。 hdu2602代码: #include<stdio.h>#include<string.h>const int MaxN = 1001;int max(int a, int b){return a > b ? a : b;}int w[MaxN];int v[MaxN];int dp[MaxN];int main(){int T;int N, V;s

poj 1511 Invitation Cards(spfa最短路)

题意是给你点与点之间的距离,求来回到点1的最短路中的边权和。 因为边很大,不能用原来的dijkstra什么的,所以用spfa来做。并且注意要用long long int 来存储。 稍微改了一下学长的模板。 stack stl 实现代码: #include<stdio.h>#include<stack>using namespace std;const int M

poj 3259 uva 558 Wormholes(bellman最短路负权回路判断)

poj 3259: 题意:John的农场里n块地,m条路连接两块地,w个虫洞,虫洞是一条单向路,不但会把你传送到目的地,而且时间会倒退Ts。 任务是求你会不会在从某块地出发后又回来,看到了离开之前的自己。 判断树中是否存在负权回路就ok了。 bellman代码: #include<stdio.h>const int MaxN = 501;//农场数const int

poj 1258 Agri-Net(最小生成树模板代码)

感觉用这题来当模板更适合。 题意就是给你邻接矩阵求最小生成树啦。~ prim代码:效率很高。172k...0ms。 #include<stdio.h>#include<algorithm>using namespace std;const int MaxN = 101;const int INF = 0x3f3f3f3f;int g[MaxN][MaxN];int n

poj 1287 Networking(prim or kruscal最小生成树)

题意给你点与点间距离,求最小生成树。 注意点是,两点之间可能有不同的路,输入的时候选择最小的,和之前有道最短路WA的题目类似。 prim代码: #include<stdio.h>const int MaxN = 51;const int INF = 0x3f3f3f3f;int g[MaxN][MaxN];int P;int prim(){bool vis[MaxN];