POJ 3261 可重叠的 k 次最长重复子串【后缀数组】

这也是一道例题

给定一个字符串,求至少出现 k 次的最长重复子串,这 k 个子串可以重叠。
算法分析:
这题的做法和上一题差不多,也是先二分答案,然后将后缀分成若干组。不
同的是,这里要判断的是有没有一个组的后缀个数不小于 k。如果有,那么存在
k 个相同的子串满足条件,否则不存在。这个做法的时间复杂度为 O(nlogn)。

Source Code:

//#pragma comment(linker, "/STACK:16777216") //for c++ Compiler
#include <stdio.h>
#include <iostream>
#include <fstream>
#include <cstring>
#include <cmath>
#include <stack>
#include <string>
#include <map>
#include <set>
#include <list>
#include <queue>
#include <vector>
#include <algorithm>
#define Max(a,b) (((a) > (b)) ? (a) : (b))
#define Min(a,b) (((a) < (b)) ? (a) : (b))
#define Abs(x) (((x) > 0) ? (x) : (-(x)))
#define MOD 1000000007
#define pi acos(-1.0)

using namespace std;

typedef long long           ll      ;
typedef unsigned long long  ull     ;
typedef unsigned int        uint    ;
typedef unsigned char       uchar   ;

template<class T> inline void checkmin(T &a,T b){if(a>b) a=b;}
template<class T> inline void checkmax(T &a,T b){if(a<b) a=b;}

const double eps = 1e-7      ;
const int N = 1              ;
const int M = 200000         ;
const ll P = 10000000097ll   ;
const int INF = 0x3f3f3f3f   ;

int a[M], sa[M], h[M], rank[M];

void radix(int *str, int *a, int *b, int n, int m){
    static int count[M];
    int i;
    memset(count, 0, sizeof(count));
    for(i = 0; i < n; ++i)  ++count[str[a[i]]];
    for(i = 1; i <= m; ++i) count[i] += count[i - 1];
    for(i = n - 1; i >= 0; --i) b[--count[str[a[i]]]] = a[i];
}

void suffix_array(int *str, int *sa, int n, int m){
    static int a[M], b[M];
    int i, j;
    for(i = 0; i < n; ++i)  rank[i] = i;
    radix(str, rank, sa, n, m);

    rank[sa[0]] = 0;
    for(i = 1; i < n; ++i)  rank[sa[i]] = rank[sa[i - 1]] + (str[sa[i]] != str[sa[i - 1]]);
    for(i = 0; 1<<i < n; ++i){
        for(j = 0; j < n; ++j){
            a[j] = rank[j] + 1;
            b[j] = j + (1 << i) >= n ? 0 : rank[j + (1 << i)] + 1;
            sa[j] = j;
        }
        radix(b, sa, rank, n, n);
        radix(a, rank, sa, n, n);
        rank[sa[0]] = 0;
        for(j = 1; j < n; ++j){
            rank[sa[j]] = rank[sa[j - 1]] + (a[sa[j - 1]] != a[sa[j]] || b[sa[j - 1]] != b[sa[j]]);
        }
    }
}

void calc_height(int *str, int *sa, int *h, int n){
    int i, k = 0;
    h[0] = 0;
    for(i = 0; i < n; ++i){
        k = k == 0 ? 0 : k - 1;
        if(rank[i] != 0){
            while(str[i + k] == str[sa[rank[i] - 1] + k]){
                ++k;
            }
        }
        h[rank[i]] = k;
    }
}

void solve_duplicate_substr(int n){
    int i, j, pos, ans = 0;
    for(i = 0; i < n; ++i){
        if(h[rank[i]] > ans){
            ans = h[rank[i]];
            pos = i;
        }
    }
    for(i = pos; i < pos + ans; ++i){
        printf("%c", a[i]);
    }
    printf("\n");
}

void slove_update_duplicate_substr(int n, int k){
    int i, j;
    int low = 1, high = n;
    int ans = 0, pos1 = 0, pos2 = 0;
    while(low <= high){
        int mid = (low + high) / 2;
        bool flag = false;
        for(i = 0; i < n; ++i){
            if(h[i] >= mid){
                ++ans;
                if(ans >= k)    flag = true;
            }
            else    ans = 1;
        }
        if(flag)    low = mid + 1;
        else    high = mid - 1;
    }
    cout << high << endl;
}

int main(){
    int i, j, t, n, m, k;
    while(cin >> n >> k){
        for(i = 0; i < n; ++i)  cin >> a[i];
        suffix_array(a, sa, n, 256);
        calc_height(a, sa, h, n);
        slove_update_duplicate_substr(n, k);
    }
    return 0;
}
时间: 2024-10-17 18:38:34

POJ 3261 可重叠的 k 次最长重复子串【后缀数组】的相关文章

POJ 3261 Milk Patterns (求可重叠的k次最长重复子串)

Milk Patterns Time Limit: 5000MS   Memory Limit: 65536K Total Submissions: 14094   Accepted: 6244 Case Time Limit: 2000MS Description Farmer John has noticed that the quality of milk given by his cows varies from day to day. On further investigation,

poj 3261 求可重叠的k次最长重复子串

题意:求可重叠的k次最长重复子串的长度 链接:点我 和poj1743差不多 1 #include<cstdio> 2 #include<iostream> 3 #include<algorithm> 4 #include<cstring> 5 #include<cmath> 6 #include<queue> 7 #include<map> 8 using namespace std; 9 #define MOD 1000

可重叠的k次最长重复子串

题目链接:https://cn.vjudge.net/contest/318888#problem/B 题意: 约翰注意到奶牛产奶的之类是不断变化的,虽然他不能预测从当天到下一天的变化情况但是他知道变化是有规律的,牛奶的质量由一个整数表示,范围从0到1000000,现在给定一个长度为n的序列,要求找到一个最大子序列,该子序列重复出现至少k次,各个出现部分可有重叠,求最长的长度.简单来说就是可重叠的k 次最长重复子串. 简单来说就是求可重叠的k次最长重复子串 思路: 这题首先先看数据的范围,发现相

POJ - 3261 Milk Patterns (后缀数组求可重叠的 k 次最长重复子串)

Description Farmer John has noticed that the quality of milk given by his cows varies from day to day. On further investigation, he discovered that although he can't predict the quality of milk from one day to the next, there are some regular pattern

[Poj3261] [Bzoj1717] [后缀数组论文例题,USACO 2006 December Gold] Milk Patterns [后缀数组可重叠的k次最长重复子串]

和上一题(POJ1743,上一篇博客)相似,只是二分的判断条件是:是否存在一段后缀的个数不小于k 1 #include <iostream> 2 #include <algorithm> 3 #include <cstdio> 4 #include <cstdlib> 5 #include <cstring> 6 #include <cmath> 7 #include <ctime> 8 #include <map&

可重叠k次最长重复子串

题目链接:https://cn.vjudge.net/contest/318888#overview 题意:约翰注意到奶牛产奶的之类是不断变化的,虽然他不能预测从当天到下一天的变化情况但是他知道变化是有规律的,牛奶的质量由一个整数表示,范围从0到1000000,现在给定一个长度为n的序列,要求找到一个最大子序列,该子序列重复出现至少k次,各个出现部分可有重叠,求最长的长度.简单来说就是可重叠的k 次最长重复子串. 思路:直接根据09年oi论文<<后缀数组——出来字符串的有力工具>>

poj 1743 最长不重叠重复子串 后缀数组+lcp+二分

题比较容易读懂,但是建模需动点脑子: 一个子串加常数形成的子串认为跟子串相同,求最长不重叠重复子串 题目中说 is disjoint from (i.e., non-overlapping with) at least one of its other appearance(s) 意味着不能重叠,举个例子 1, 2,3,  52, 53,54 1,2, 3和 52, 53,54满足题意,差值为51 枚举差值肯定不行------看了题解明白的:: 后项减去前一项得到: 1,1,1,49,1,1  

POJ 2774 求两个串的最长公共前缀 | 后缀数组

#include<cstdio> #include<algorithm> #include<cstring> #define N 200005 using namespace std; int buf1[N],buf2[N],sa[N],rnk[N],buc[N],n,height[N],ans,belong[N]; char s[N]; void suffix_sort() { int *x=buf1,*y=buf2,m=1000; for (int i=0;i<

POJ 3294 出现在至少K个字符串中的子串

在掌握POJ 2774(两个串求最长公共子串)以及对Height数组分组后,本题还是容易想出思路的. 首先用字符集外的不同字符连接所有串,这是为了防止两个后缀在比较时超过某个字符串的分界.二分子串的长度,扫描height数组,判定是否有某个分组来源与至少K个原字符串(本题要求出现超过n的一半次). #include <iostream> #include <vector> #include <algorithm> #include <string> #inc