• 暑假 D6 T3 longest(后缀数组+二分)


    题目描述

    给定一个长为n的字符串,求其不可重叠的最长重复子串长度(即存在两个这样的子串并且不相交)

    输入格式

    仅一行,为题目描述的字符串

    输出格式

    输出一行一个整数,表示不可重叠的最长重复子串的长度

    数据范围

    对于100%的数据,保证1≤n≤100000,串的所有字符均为小写字母

    题解

    子串是后缀的前缀

    所以两个后缀的lcp就是重复子串

    如果有满足条件的长度为len的重复子串,那么就有len-1的重复子串

    具有二分性

    将sa数组按下标列出,height[i]为sa[i]和sa[i-1]的桥梁

    check函数中,将长度<mid的桥梁删去,分成了一些组,这些组内的任意两个后缀的lcp≤mid,因为两个后缀的lcp就是他们之前所有桥梁的min

    那么在组内如果找到两个后缀,他们起始位置只差≥mid,那么就可以

    求后缀数组时,c数组只开到了字符集大小,在luogu模板过了可还行

    #include<bits/stdc++.h>
    using namespace std;
    
    const int maxn=1000005;
    int n,m=26;
    char s[maxn];
    int x[maxn],y[maxn],c[maxn];
    int sa[maxn],rk[maxn],height[maxn];
    
    void get_sa(){
        for(int i=0;i<n;i++) c[x[i]=s[i]-'a']++;
        for(int i=1;i<n;i++) c[i]+=c[i-1];
        for(int i=n-1;i>=0;i--) sa[--c[x[i]]]=i;
        for(int k=1;k<=n;k<<=1){
            int p=0;
            for(int i=n-k;i<n;i++) y[p++]=i;
            for(int i=0;i<n;i++) if(sa[i]>=k) y[p++]=sa[i]-k;
            for(int i=0;i<m;i++) c[i]=0;
            for(int i=0;i<n;i++) c[x[i]]++;
            for(int i=1;i<n;i++) c[i]+=c[i-1];
            for(int i=n-1;i>=0;i--) sa[--c[x[y[i]]]]=y[i];
            swap(x,y);
            p=1;
            x[sa[0]]=0;
            for(int i=1;i<n;i++)
             x[sa[i]]= y[sa[i]]==y[sa[i-1]]&&sa[i]+k<n&&sa[i-1]+k<n&&y[sa[i]+k]==y[sa[i-1]+k] ? p-1 : p++;
            if(p==n) break;
            m=p;
        }
    }
    
    void get_h(){
        for(int i=0;i<n;i++) rk[sa[i]]=i;
        int k=0;
        for(int i=0;i<n;i++){
            if(!rk[i]) continue;
            if(k) k--;
            int j=sa[rk[i]-1];
            while(j+k<n&&i+k<n&&s[i+k]==s[j+k]) k++;
            height[rk[i]]=k;
        }
    }
    
    bool check(int x){
        int mx,mi;
        mx=mi=sa[0];
        for(int i=1;i<n;i++){
            if(height[i]>=x){
                mx=max(mx,sa[i]);
                mi=min(mi,sa[i]);
            }
            else mx=mi=sa[i];
            if(mx-mi>=x) return true;
        }
        return false;
    }
    
    int main(){
        freopen("longest.in","r",stdin);
        freopen("longest.out","w",stdout);
        scanf("%s",s);
        n=strlen(s);
        get_sa();
        get_h();
        int l=0,r=n,ans=0;
        while(l<=r){
            int mid=(l+r)>>1;
            if(check(mid)) ans=mid,l=mid+1;
            else r=mid-1;
        }
        printf("%d",ans);
    }
    View Code
  • 相关阅读:
    IIS下 多站点 利用虚拟目录 访问共用(图片)文件夹
    [转]Prevent opening multiple windows in ASP.NET
    [转]使用 WebForms ReportViewer 控件
    ALTER TABLE ADD 增加多个字段 外键约束
    获取数据库内某时间点后修改的存储过程
    SQL Server 示例数据库 AdventureWorks 数据字典
    [转]MVC 3.0 在各个版本IIS中的部署
    [转]使用jQuery.ajax傳送物件陣列給ASP.NET MVC
    [转]报表统计(sql面试题)
    扑克游戏 – 打联手
  • 原文地址:https://www.cnblogs.com/sto324/p/11191625.html
Copyright © 2020-2023  润新知