FZU 2128 最长子串

Posted 小小八

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了FZU 2128 最长子串相关的知识,希望对你有一定的参考价值。

题目链接:最长子串 

思路:依次找出每个子串的在字符串中的首尾地址,所有子串先按照尾地址从小到大排序。然后首地址从小到大排。

遍历一遍每个子串的首地址和它后面相邻子串的尾地址之差-1, 第一个子串的首地址,字符串长度-最后一个子串的首地址-1的最大值就是ans。

st1----------ed1

-------st2------------ed2

例如这种情况说明,可能出现的一个ans 就是 ed2和st1之间的字符个数。这时候没有ed2最后一个字符,st1第一个字符,所以不包含str1和str2.

关于找每个子串的位置,有两种方法,kmp和strstr.

会找到多少个子串位置呢,最大当然不是n,而是字符串长度!结构体数组开小,RE了一个半点~~~

 

kmp AC 代码:

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

const int maxn = 1000005;

struct Node {
	int st, ed;
} node[maxn];

char s[maxn], t[1005][105];
int n, cnt, next[105];

bool cmp(Node a, Node b) {
    if (a.ed != b.ed)
    return a.ed < b.ed;
    else return a.st <= b.st;
}

void get_next(char p[]) {
    memset(next, 0, sizeof(next));
    int len = strlen(p);
    int i = 0;
    next[0] = -1;
    int k = -1;
    while(i<len) {
        if (k == -1 || p[i] == p[k]) {
            i++;
            k++;
            next[i] = k;
        }
        else k = next[k];
    }
}

void kmp(char s[], char p[]) {
    int lens = strlen(s);
    int lenp = strlen(p);
    int i = 0, j = 0;
    get_next(p);

    while(i<lens && j<lenp) {
        if (j == -1 || s[i] == p[j]) {
            i++;
            j++;
        }
        else j = next[j];
       if (j == lenp) {
            node[cnt].st = i-lenp;
            node[cnt].ed = i-1;
            cnt++;
            j = next[j];
        }
    }
}


int main() {
	while (scanf("%s", s) == 1) {
		scanf("%d", &n);
		for (int i = 0; i < n; i++)
			scanf("%s", t[i]);
		cnt = 0;
		for (int i = 0; i < n; i++) {
			kmp(s, t[i]);
		}
		sort(node, node + cnt, cmp);
		int ans = -1;
		for (int i=0; i<cnt-1; ++i) {
            int st = node[i].st;
            int ed = node[i+1].ed;
            ans = max(ans, ed - st - 1);
        }

        int len = strlen(s);
        if (cnt > 0) {
            ans = max(ans, node[0].ed);
            ans = max(ans, len-node[cnt-1].st-1);
        }
        else ans = len;
        printf("%d\n", ans);
	}
}

  

strstr函数处理AC代码:

#include <stdio.h>
#include <string.h>
#include <iostream>
#include <algorithm>
using namespace std;

char s[1000010], p[110];
char str[1000010];

struct Node {
    int st, ed;
}node[1000010];

int next[110];
int cnt;

bool cmp(Node a, Node b) {
    if (a.ed != b.ed)
    return a.ed < b.ed;
    else return a.st <= b.st;
}

bool check(char s[], char p[]) {
    if (strstr(s, p))
        return true;
    return false;
}

void solve(char s[], char p[], int pre) { // 开始没设pre 参数。这样每次找到的位置只是相对当前字符串的,不能直接设为node[cnt].st 和 node[cnt].ed.
    int lenp = strlen(p);
    if (check(s, p)) {
        int num = strstr(s, p) - s;
        node[cnt].st = num + pre;
        node[cnt].ed = num + pre + lenp - 1;
        cnt++;
        solve(s+num+lenp, p, pre+num+lenp);
    }
}

int main() {
    int n;
    while(~scanf("%s", s)) {
        cnt = 0;
        scanf("%d", &n);
        for (int i=0; i<n; ++i) {
            scanf("%s", p);
            solve(s, p, 0);
        }
        sort(node, node+cnt, cmp);
        int ans = 0;

        for (int i=0; i<cnt-1; ++i) {
            int st = node[i].st;
            int ed = node[i+1].ed;
            ans = max(ans, ed - st - 1);
        }
        int len = strlen(s);
        if (cnt > 0) {
            ans = max(ans, node[0].ed);
            ans = max(ans, len-node[cnt-1].st-1);
        }
        else ans = len;
        printf("%d\n", ans);
    }
    return 0;
}

  

后话,感觉无论是kmp还是strstr都明显会超时,如kmp是 n*strlen(str),最大是10^3*10^6。然,并没有,而且题解貌似都是这样的解........

挺好的题,会的kmp,需要思考的方案。

以上是关于FZU 2128 最长子串的主要内容,如果未能解决你的问题,请参考以下文章

《算法竞赛入门经典》3.3最长回文子串

FZU 2216 The Longest Straight(最长直道)

动态规划——最长公共子序列与最长公共子串 (含Python实现代码)

代码题(56)— 最长重复子串无重复字符的最长子串

最长回文字串暴力

两个字符串的最长公共子串python代码