zoukankan      html  css  js  c++  java
  • 题解-牛奶模式


    (〇)题目描述

    题目传送门

    简单讲解一下题意:

    给出一个字符串,求最长至少出现了 (k) 的子串(可重叠)。

    (一)解题思路

    这题需要我们在一个模式串中找相同的子串,很容易就能想到后缀数组。

    那么,如何找至少重复出现 (k) 次的子串呢?

    考虑二分子串的长度,看看答案是否具有单调性。

    如果长度为 (len) 的子串出现了 (k) 次,那么一定有长度小于 (len) 的子串出现了 (k) 次(这些子串可以是长度为 (len) 的子串的子串)

    这样题目就变成了判定性问题,我们只需判断是否有长度为 (len) 子串出现次数(geqslant k)

    问题又来了,如何判断字符串中是否有至少重复出现 (k) 次的子串呢?

    要找重复 (k) 次的子串,其实就是找 (k) 个相同的子串。找相同的子串,可以考虑使用后缀数组。对于得到的 (height[~]) 数组,使用分组的方法,使得每组中的每个后缀的最长公共前缀都 (geqslant len) ,再判断是否有一组中的后缀数量 (geqslant k) 即可。

    (二)解题方法

    做法:二分答案+后缀数组

    时间复杂度:(Theta(nlog_2{n}))

    二分答案不必讲,求后缀数组和 (height[~]) 数组可以参考论文或者学习总结-后缀数组

    然后...就没有然后了,直接上代码:

    #include<bits/stdc++.h>
    using namespace std;
    const int maxn = 1000005;
    int sa[maxn], rak[maxn], sum[maxn], height[maxn];
    int key2[maxn], newRak[maxn], a[maxn];
    int N, K, M;
    bool cmp(int a, int b, int l)
    {
        if(rak[a] != rak[b]) return false;
        if((a+l > N) xor (b+l > N)) return false;
        if(a+l > N and b+l > N) return true;
        return rak[a+l] == rak[b+l];
    }
    void getHeight()
    {
        int k = 0;
        for(int i=1; i<=N; i++)
        {
            if(rak[i] == 1) {height[rak[i]] = 0; continue;}
            int j = sa[rak[i]-1];
            while(a[i+k] == a[j+k]) k++;
            height[rak[i]] = k;
            if(k != 0) k--;
        }
    }
    bool ok(int x)
    {
        int cnt = 1;
        for(int i=2; i<=N; i++)
        {
            if(height[i] < x) cnt = 1;
            else cnt++;
            if(cnt >= K) return true;
        }
        return false;
    }
    int main()
    {
        scanf("%d%d",&N,&K);
        for(int i=1; i<=N; i++) scanf("%d",&a[i]);
        M = max(1000000, N);
        for(int i=1; i<=N; i++) sum[rak[i] = a[i]]++;
        for(int i=1; i<=M; i++) sum[i] += sum[i-1];
        for(int i=N; i>=1; i--) sa[sum[rak[i]]--] = i;
        for(int l=1; l<N; l<<=1)
        {
            int k = 0;
            for(int i=N-l+1; i<=N; i++) key2[++k] = i;
            for(int i=1; i<=N; i++) if(sa[i] > l) key2[++k] = sa[i]-l;
            for(int i=1; i<=M; i++) sum[i] = 0;
            for(int i=1; i<=N; i++) sum[rak[i]]++;
            for(int i=1; i<=M; i++) sum[i] += sum[i-1];
            for(int i=N; i>=1; i--) sa[sum[rak[key2[i]]]--] = key2[i];
            int rk = 1;
            newRak[sa[1]] = 1;
            for(int i=2; i<=N; i++)
                if(cmp(sa[i], sa[i-1], l)) newRak[sa[i]] = rk;
                else newRak[sa[i]] = ++rk;
            for(int i=1; i<=N; i++) rak[i] = newRak[i];
            if(rk == N) break;
        }
        getHeight();
        int l = 0, r = N+1;
        while(l+1 < r)
        {
            int mid = (l+r)>>1;
            if(ok(mid)) l = mid;
            else r = mid;
        }
        printf("%d", l);
        return 0;
    } 
    
  • 相关阅读:
    分享一些曾经设计的ASP.NET自定义服务端控件(附源程序下载)
    使用SuperSocket实现TLV自定义协议网络通信的Demo
    让Silverlight支持GB2312中文编码
    在Sqlite中通过Replace来实现插入和更新
    在VS2010项目中引用Lib静态库(以Openssl为例)
    金融系统中PBOC/EMV的TLV的算法实现(含C++/C#)
    在Windows下C++实现UNIX中的GZ格式的解压缩(附工具)
    Opensuse网络配置备忘
    项目管理理论与实践系列文章索引
    让Windows远程访问Opensuse桌面的解决办法
  • 原文地址:https://www.cnblogs.com/GDOI2018/p/10350656.html
Copyright © 2011-2022 走看看