【发布时间】:2018-10-02 22:04:39
【问题描述】:
我得到了n 字符串(n>=2 和 na 和 b。在这组字符串中,我必须找到所有字符串中存在的最长公共子字符串的长度。保证存在解决方案。我们来看一个例子:
n=4
abbabaaaaabb
aaaababab
bbbbaaaab
aaaaaaabaaab
The result is 5 (because the longest common substring is "aaaab").
我不必打印(甚至知道)子字符串,我只需要打印它的长度。
同时给出结果不能大于60,即使每个字符串的长度可以高达13 000。
我尝试的是:我找到给定字符串中任何字符串的最小长度,然后将其与60 进行比较,然后选择两者之间的最小值为starting point。然后我开始取第一个字符串的序列,第一个字符串的每个序列的长度为len,其中len 取值从starting point 到1。在每次迭代中,我采用长度为len 的第一个字符串的所有可能序列,并将其用作pattern。使用 KMP 算法(因此,复杂度为 O(n+m)),我遍历所有其他字符串(从 2 到 n)并检查是否在字符串 i 中找到 pattern。每当找不到时,我会中断迭代并尝试下一个长度为len 的可用序列,或者,如果没有,我减少len 并尝试所有长度为新的、减少的值@ 的序列987654344@。但是如果它匹配,我停止程序并打印长度len,因为我们从可能的最长长度开始,每一步递减,所以我们找到的第一个匹配代表最大可能的长度是合乎逻辑的。这是代码(但这并不重要,因为这种方法不够好;我知道我不应该使用using namespace std,但它并没有真正影响这个程序,所以我只是没有打扰):
#include <iostream>
#include <string>
#define nmax 50001
#define result_max 60
using namespace std;
int n,m,lps[nmax],starting_point,len;
string a[nmax],pattern,str;
void create_lps() {
lps[0]=0;
unsigned int len=0,i=1;
while (i < pattern.length()) {
if (pattern[i] == pattern[len]) {
len++;
lps[i] = len;
i++;
}
else {
if (len != 0) {
len = lps[len-1];
}
else {
lps[i] = 0;
i++;
}
}
}
}
bool kmp_MatchOrNot(int index) {
unsigned int i=0,j=0;
while (i < a[index].length()) {
if (pattern[j] == a[index][i]) {
j++;
i++;
}
if (j == pattern.length()) {
return true;
}
else if (i<a[index].length() && pattern[j]!=a[index][i]){
if (j != 0) {
j = lps[j-1];
}
else {
i++;
}
}
}
return false;
}
int main()
{
int i,left,n;
unsigned int minim = nmax;
bool solution;
cin>>n;
for (i=1;i<=n;i++) {
cin>>a[i];
if (a[i].length() < minim) {
minim = a[i].length();
}
}
if (minim < result_max) starting_point = minim;
else starting_point = result_max;
for (len=starting_point; len>=1; len--) {
for (left=0; (unsigned)left<=a[1].length()-len; left++) {
pattern = a[1].substr(left,len);
solution = true;
for (i=2;i<=n;i++) {
if (pattern.length() > a[i].length()) {
solution = false;
break;
}
else {
create_lps();
if (kmp_MatchOrNot(i) == false) {
solution = false;
break;
}
}
}
if (solution == true) {
cout<<len;
return 0;
}
}
}
return 0;
}
事情是这样的:程序运行正常并且给出了正确的结果,但是当我在网站上发送代码时,它给出了“超出时间限制”的错误,所以我只得到了一半的分数。
这让我相信,为了以更好的时间复杂度解决问题,我必须利用字符串的字母只能是a或b这一事实,因为它看起来就像我没有使用的一个非常大的东西,但我不知道我该如何使用这些信息。我将不胜感激。
【问题讨论】:
-
快速浏览一下您的代码有
for{for{for}},它似乎大致为 O(lgn^2)。这个问题有一个非常有效的解决方案,接近 O(n) 你应该看到this。它是解决这个问题的经典算法 -
@user3386109 你是对的,我没有检查就输入了。示例的结果确实是 5。对不起。
-
@138 嗯,这就是我使用的算法,KMP(你链接的那个)。但这不仅仅是搜索和检查。我必须找到 N 个字符串之间最长的公共子字符串,所以还有很多工作要做。第一个'for'设置当前模式的长度(第一个字符串);第二个“for”选择当前模式(第一个字符串的)开始的位置。我这样做是为了获取实际模式并构建“lps []”数组。第三个“for”检查当前模式(第一个字符串)是否与所有其他字符串(从 2 到 n)匹配。我不知道如何才能缩短时间。
-
我认为这可以通过trie 来完成。使用最短的字符串来构建 trie。然后处理其他字符串,标记已访问的节点,但不添加任何新节点。最后,遍历 trie。所有字符串访问过的最深节点的深度就是答案。
-
@user3386109 如果没有记忆,比较后缀树的成本类似于比较尝试。有了记忆,它绝对更快。如果您重用数据结构中恰好相同的部分,则后缀树数据结构就是 trie 数据结构,因此解决方案在概念上是相同的,尽管后缀树是绝对的赢家。
标签: c++ string algorithm optimization longest-substring