zoukankan      html  css  js  c++  java
  • 暑假 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
  • 相关阅读:
    小心SQL SERVER 2014新特性——基数评估引起一些性能问题
    SQL SERVER使用ODBC 驱动建立的链接服务器调用存储过程时参数不能为NULL值
    Windows Server 2012 Recycle Bin corrupted
    SQL SERVER CHAR ( integer_expression )各版本返回值差异的案例
    SQL Server 2008 R2 升级到 Service Pack 3后Report Builder启动不了
    MySQL如何导出带日期格式的文件
    ORACLE TO_CHAR函数格式化数字的出现空格的原因
    Linux监控工具介绍系列——smem
    Linux命令学习总结:dos2unix
    Linux命令学习总结:hexdump
  • 原文地址:https://www.cnblogs.com/sto324/p/11191625.html
Copyright © 2011-2022 走看看