题意 : 给出一个长度为 N 的序列,再给出一个 K 要求求出出现了至少 K 次的最长可重叠子串的长度

分析 : 后缀数组套路题,思路是二分长度再对于每一个长度进行判断,判断过程就是对于 Height 数组进行限定长度的分组策略,如果有哪一组的个数 ≥  k 则说明可行!

分组要考虑到一个事实,对于每一个后缀,与其相匹配能够产生最长的LCP长度的串肯定是在后缀数组中排名与其相邻。

一开始对分组的理解有误,所以想了一个错误做法 ==>

遍历一下 Height 将值 ≥ (当前二分长度) 的做一次贡献即 cnt++ ,若最后 cnt ≥ K 说明可行。当然这个肯定是炸了.......

下面说说我对于 Height 分组的理解吧,就看上面的图,如果当前 K == 2,那么第一组的含义是什么?换句话说就是为什么那么些个后缀要属于一组?可以看出第一组里面的 Height 值都不会小于 K ,实际的意义呢应当是第一组里面的有一个长度为 2 (不小于K)的共同前缀,即 “aa” ,那么是不是 “aa” 这个子串可重叠地出现了 cnt 次(cnt为第一组的后缀个数),可能你已经有点体会到分组的意义了!那么有没有可能有些前缀是 “aa” 但是没有被分进第一组呢?看见上面红字描述的事实么?根据上面的那个事实,而且 Height 的下标是根据排名有序的这个特点(有序的意思就是从小到大遍历 Height 实际传进去的下标就是排名!即 Height[i],i是表示第 i 名的后缀),我们就知道这样的事情不会发生,且分出来的组肯定的“连续的块”,即不会有这一组的元素在其他地方的可能性!

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

int sa[maxn], s[maxn], wa[maxn], Ws[maxn], wv[maxn], wb[maxn];
int Rank[maxn], height[maxn];

bool cmp(int r[], int a, int b, int l){ return r[a] == r[b] && r[a+l] == r[b+l]; }
void da(int r[], int sa[], int n, int m)
{
    int i, j, p, *x = wa, *y = wb;
    ; i < m; ++i) Ws[i] = ;
    ; i < n; ++i) Ws[x[i]=r[i]]++;
    ; i < m; ++i) Ws[i] += Ws[i-];
    ; i >= ; --i) sa[--Ws[x[i]]] = i;
    , p = ; p < n; j *= , m = p)
    {
        , i = n - j; i < n; ++i) y[p++] = i;
        ; i < n; ++i) if (sa[i] >= j) y[p++] = sa[i] - j;
        ; i < n; ++i) wv[i] = x[y[i]];
        ; i < m; ++i) Ws[i] = ;
        ; i < n; ++i) Ws[wv[i]]++;
        ; i < m; ++i) Ws[i] += Ws[i-];
        ; i >= ; --i) sa[--Ws[wv[i]]] = y[i];
        , x[sa[]] = , i = ; i < n; ++i)
            x[sa[i]] = cmp(y, sa[i-], sa[i], j) ? p- : p++;
    }
}
void calheight(int r[], int sa[], int n)
{
    ;
    ; i <= n; ++i) Rank[sa[i]] = i;
    ; i < n; height[Rank[i++]] = k)
        , j = sa[Rank[i]-]; r[i+k] == r[j+k]; k++);
}

bool IsOk(int len, int n, int aim)
{
    ;
//    for(int i=2; i<=n; i++){ //错误的!
//        if(height[i] >= len)
//            if(++cnt >= aim)
//                return true;
//    }return false;
    ; i<=n; i++){
        if(height[i] >= len){ if(++cnt >= aim) return true; }
        ;
    }return false;
}

int arr[maxn];
int main(void)
{
    int N, K;
    while(~scanf("%d %d", &N, &K)){

        ; i<N; i++)
            scanf("%d", &arr[i]);

        da(arr, sa, N+, );
        calheight(arr, sa, N);

        , R = N, ans = -;
        while(L <= R){
            );
            ;
            ;
        }
        ans==-? puts(") : printf("%d\n", ans);
    }
    ;
}

题目单个元素的值能达到 1e6 这么大,数组按这个开还勉强OK,但是这里还是要学学离散化的姿势!

离散化版:

#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
;
struct st{
    int ord, val;
    bool operator < (const st &rhs) const {
        return this->val < rhs.val;
    };
}arr[maxn];

int sa[maxn], s[maxn], wa[maxn], Ws[maxn], wv[maxn], wb[maxn];
int Rank[maxn], height[maxn];

bool cmp(int r[], int a, int b, int l){ return r[a] == r[b] && r[a+l] == r[b+l]; }
void da(int r[], int sa[], int n, int m)
{
    int i, j, p, *x = wa, *y = wb;
    ; i < m; ++i) Ws[i] = ;
    ; i < n; ++i) Ws[x[i]=r[i]]++;
    ; i < m; ++i) Ws[i] += Ws[i-];
    ; i >= ; --i) sa[--Ws[x[i]]] = i;
    , p = ; p < n; j *= , m = p)
    {
        , i = n - j; i < n; ++i) y[p++] = i;
        ; i < n; ++i) if (sa[i] >= j) y[p++] = sa[i] - j;
        ; i < n; ++i) wv[i] = x[y[i]];
        ; i < m; ++i) Ws[i] = ;
        ; i < n; ++i) Ws[wv[i]]++;
        ; i < m; ++i) Ws[i] += Ws[i-];
        ; i >= ; --i) sa[--Ws[wv[i]]] = y[i];
        , x[sa[]] = , i = ; i < n; ++i)
            x[sa[i]] = cmp(y, sa[i-], sa[i], j) ? p- : p++;
    }
}
void calheight(int r[], int sa[], int n)
{
    ;
    ; i <= n; ++i) Rank[sa[i]] = i;
    ; i < n; height[Rank[i++]] = k)
        , j = sa[Rank[i]-]; r[i+k] == r[j+k]; k++);
}

bool IsOk(int len, int n, int aim)
{
    ;
    ; i<=n; i++){
        if(height[i] >= len)
            { if(++cnt >= aim) return true; }
        ;
    }return false;
}

int r[maxn];
int main(void)
{
    int N, K;
    while(~scanf("%d %d", &N, &K)){
        ; i<N; i++){
            scanf("%d", &arr[i].val);
            arr[i].ord = i;
        }

        ;
        sort(arr, arr+N);
        ; i<N; i++)
             && arr[i].val == arr[i-].val) r[arr[i].ord] = num; ///注意相等的时候如何处理
            else r[arr[i].ord] = ++num;

        da(r, sa, N+, num+);
        calheight(r, sa, N);

        , R = N, ans = -;
        while(L <= R){
            );
            ;
            ;
        }
        ans==-? puts(") : printf("%d\n", ans);
    }
    ;
}

POJ 3261 Milk Patterns ( 后缀数组 && 出现k次最长可重叠子串长度 )的更多相关文章

  1. POJ 3261 Milk Patterns 后缀数组求 一个串种 最长可重复子串重复至少k次

    Milk Patterns   Description Farmer John has noticed that the quality of milk given by his cows varie ...

  2. Poj 3261 Milk Patterns(后缀数组+二分答案)

    Milk Patterns Case Time Limit: 2000MS Description Farmer John has noticed that the quality of milk g ...

  3. POJ 3261 Milk Patterns(后缀数组+单调队列)

    题意 找出出现k次的可重叠的最长子串的长度 题解 用后缀数组. 然后求出heigth数组. 跑单调队列就行了.找出每k个数中最小的数的最大值.就是个滑动窗口啊 (不知道为什么有人写二分,其实写啥都差不 ...

  4. poj 3261 Milk Patterns 后缀数组 + 二分

    题目链接 题目描述 给定一个字符串,求至少出现 \(k\) 次的最长重复子串,这 \(k\) 个子串可以重叠. 思路 二分 子串长度,据其将 \(h\) 数组 分组,判断是否存在一组其大小 \(\ge ...

  5. POJ3261 Milk Patterns —— 后缀数组 出现k次且可重叠的最长子串

    题目链接:https://vjudge.net/problem/POJ-3261 Milk Patterns Time Limit: 5000MS   Memory Limit: 65536K Tot ...

  6. POJ 3261 Milk Patterns (求可重叠的k次最长重复子串)+后缀数组模板

    Milk Patterns Time Limit: 5000MS   Memory Limit: 65536K Total Submissions: 7586   Accepted: 3448 Cas ...

  7. POJ 3261 Milk Patterns 【后缀数组 最长可重叠子串】

    题目题目:http://poj.org/problem?id=3261 Milk Patterns Time Limit: 5000MS Memory Limit: 65536K Total Subm ...

  8. poj3261 Milk Patterns 后缀数组求可重叠的k次最长重复子串

    题目链接:http://poj.org/problem?id=3261 思路: 后缀数组的很好的一道入门题目 先利用模板求出sa数组和height数组 然后二分答案(即对于可能出现的重复长度进行二分) ...

  9. poj 3261 Milk Patterns(后缀数组)(k次的最长重复子串)

    Milk Patterns Time Limit: 5000MS   Memory Limit: 65536K Total Submissions: 7938   Accepted: 3598 Cas ...

随机推荐

  1. Scala的to和until

    object test03 { def main(args: Array[String]): Unit = { //to 每次迭代为1 val to1= to print("to1" ...

  2. Golang基本类型整理

    总是用的用的就模糊了,不知道基本的类型有哪些,看来要反反复复弄几次. Golang基本类型整理 基本类型以及定义变量需要注意的 对于基本类型的介绍,感觉这个博客讲的比较透彻,基本上都是从源码的角度来入 ...

  3. angulart 常用

    angular: 使用 echarts npm install echarts --save // 安装declare const echarts: any; // 引入https://www.ech ...

  4. 【监控笔记】【2.3】扩展事件——慢查询SQL(执行超过3S的SQL)

    --sql server 2008及以上才支持,2012及以上才支持GUI界面 msdn 扩展事件:点击打开链接 [1]T-SQL实现 基于 rpc_completed(远程过程调用已完成时发生) 事 ...

  5. vue组件命名和传值

    一.vue组件命名: 组件有好几种命名方式, 可以使用 component-vue (短横线分隔命名).componentVue  (驼峰式命名) 或ComponentVue  (单词首字母) 因为h ...

  6. [FJOI2007]轮状病毒 题解(dp(找规律)+高精度)

    [FJOI2007]轮状病毒 题解(dp(找规律)+高精度) 标签:题解 阅读体验:https://zybuluo.com/Junlier/note/1335733 没什么好说的,直接把规律找出来,有 ...

  7. 小白如何入门 Python 爬虫?

    本文针对初学者,我会用最简单的案例告诉你如何入门python爬虫! 想要入门Python 爬虫首先需要解决四个问题 熟悉python编程 了解HTML 了解网络爬虫的基本原理 学习使用python爬虫 ...

  8. Python 入门之 内置模块 -- time模块

    Python 入门之 内置模块 -- time模块 1.time模块 ​ time翻译过来就是时间,这个模块是与时间相关的模块 import time # 内置模块 -- 标准库 (1)time.ti ...

  9. web 过滤器 Filter、 Spring 拦截器 interceptor

    1.过滤器(Filter)(在web.xml中注册过滤器) 首先说一下Filter的使用地方,我们在配置web.xml时,总会配置下面一段设置字符编码,不然会导致乱码问题: <filter> ...

  10. C#导出大量数据到excel,怎么提升性能

    一,要提升性能,我们先要知道耗时的地方在哪里 1,数据库查询,2,把数据组合成新集合循环嵌套太多 二,那我们怎么优化呢? 一,数据库查询,1>,数据库查询:如果数据量小,我们可以用临时datat ...