所有既是前缀又是后缀的子串 KMP POJ 2752 Seek the Name, Seek the Fame

本文介绍了一种用于找出字符串中既是前缀也是后缀的子串的方法。通过构建next数组,利用KMP算法原理,递归查找所有符合条件的子串,并按长度从小到大输出。

http://poj.org/problem?id=2752

大致题意:
    给出一个字符串str,求出str中存在多少子串,使得这些子串既是str的前缀,又是str的后缀。从小到大依次输出这些子串的长度。

 

大致思路:
    如左图,假设黑色线来代表字符串str,其长度是len,红色线的长度代表next[len],根据next数组定义易得前缀的next[len]长度的子串和后缀next[len]长度的子串完全相同(也就是两条线所对应的位置)。我们再求出next[len]位置处的next值,也就是图中蓝线对应的长度。同样可以得到两个蓝线对应的子串肯定完全相同,又由于第二段蓝线属于左侧红线的后缀,所以又能得到它肯定也是整个字符串的后缀。

    所以对于这道题,求出len处的next值,并递归的向下求出所有的next值,得到的就是答案。

#include<iostream>
#include<cstring>
#include<stack>
#include<cstdio>
using namespace std;
const int nMax=1000005;
char pat[nMax];
int lenp,next[nMax];

void get_next(){
    int i,j=-1;
    next[0]=-1;
    for(i=1;i<=lenp;i++){
        while(j>-1&&pat[j+1]!=pat[i])j=next[j];
        if(pat[j+1]==pat[i])j++;
        next[i]=j;
    }
}

stack<int>ans;
int main(){
    while(scanf("%s",pat)!=EOF){
        lenp=strlen(pat);
        get_next();
        int j=next[lenp-1];
        while(j!=-1){
            ans.push(j);
            j=next[j];
        }
        while(!ans.empty()){
            cout<<ans.top()+1<<" ";
            ans.pop();
        }cout<<lenp<<endl;
    }
    return 0;
}


### 优化后的C++代码实现 以下是对问题的详细解答,包括优化后的代码实现以及对多字符串输入场景的支持。 #### 1. 问题分析与解决思路 用户的需求是使用KMP算法求解既是前缀又是后缀子串长度,并将其扩展到适用于总长不超过4×10^5的多字符串输入场景。为此,需要满足以下要求: - 针对每个字符串,计算其既是前缀又是后缀子串长度。 - 处理多个字符串时,确保整体时间复杂度在可接受范围内。 - 使用KMP算法的核心思想(nxt数组)来高效解决问题[^2]。 以下是优化后的代码实现: ```cpp #include <iostream> #include <vector> #include <string> #include <algorithm> using namespace std; // 构造 nxt 数组,用于计算模式串的最长公共前后缀信息。 vector<int> computeNxt(const string& str) { int n = str.size(); vector<int> nxt(n, 0); int j = 0; // j 表示最长相等前后缀的长度 for (int i = 1; i < n; ++i) { while (j > 0 && str[i] != str[j]) { j = nxt[j - 1]; // 回退到上一个匹配的位置 } if (str[i] == str[j]) { j++; } nxt[i] = j; } return nxt; } // 求解既是前缀又是后缀子串长度 vector<int> findPrefixSuffixLengths(const string& str) { vector<int> nxt = computeNxt(str); int n = str.size(); vector<int> result; int len = nxt[n - 1]; while (len > 0) { if (str.substr(0, len) == str.substr(n - len, len)) { result.push_back(len); } len = nxt[len - 1]; } return result; } // 主函数处理多字符串输入 int main() { int n; cin >> n; // 输入字符串数量 vector<string> strings(n); for (int i = 0; i < n; ++i) { cin >> strings[i]; // 输入每个字符串 } // 对每个字符串进行处理并输出结果 for (const string& str : strings) { vector<int> lengths = findPrefixSuffixLengths(str); if (lengths.empty()) { cout << "没有既是前缀又是后缀子串" << endl; } else { cout << "既是前缀又是后缀子串长度为: "; for (int len : lengths) { cout << len << " "; } cout << endl; } } return 0; } ``` --- #### 2. 代码功能详解 - **`computeNxt` 函数**:此函数实现了KMP算法的核心部分——构造nxt数组。nxt数组记录了模式串中每个位置的最长公共前后缀长度,这是KMP算法高效匹配的关键[^2]。 - **`findPrefixSuffixLengths` 函数**:利用nxt数组查找字符串中所有既是前缀又是后缀子串长度。通过不断回退到更短的前缀位置,确保不会遗漏任何可能的匹配子串。 - **`main` 函数**:主函数负责接收多个字符串输入,并对每个字符串调用上述两个函数进行处理。最终输出每个字符串对应的既是前缀又是后缀子串长度。 --- #### 3. 时间复杂度分析 - **单个字符串处理**:对于长度为`m`的字符串,`computeNxt`函数的时间复杂度为O(m),而`findPrefixSuffixLengths`函数的时间复杂度也为O(m)。因此,单个字符串的处理时间为O(m)[^2]。 - **多个字符串处理**:假设共有`n`个字符串,且所有字符串的总长度为`L`,则整体时间复杂度为O(L)。由于题目限制总长度不超过4×10^5,该算法能够在合理时间内完成计算。 --- #### 4. 优化点说明 - **避免重复计算**:通过一次性计算nxt数组,减少了冗余操作。 - **多字符串支持**:将单字符串处理逻辑封装为独立函数,便于扩展到多字符串场景。 - **输入输出优化**:使用标准输入输出流,确保代码简洁易读。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值