• 洛谷P2463 [SDOI2008]Sandy的卡片(后缀数组SA + 差分 + 二分答案)


    题目链接:https://www.luogu.org/problem/P2463

    【题意】

    求出N个串中都出现的相同子串的最长长度,相同子串的定义如题:所有元素加上一个数变成另一个,则这两个串相同,可以很简单的得出,差分后的串相同即相同。

    【思路】

    首先肯定是要对N个串分别进行差分,然后将N个串合并成一个串,首尾相接即可,但要标记那些数属于哪一个Mi(后边要进行check),这里呢要注意,记得将串分隔开来,

    不然会WA,这里我用的分隔方法是在串之间加0,合并完成后,题目就可变成求最长的不重叠的重复N次的最长子串长度。因为如果长度为k的串出现了N次,那么他的前缀

    也一定出现了N次,所以此题满足单调性,可以进行二分答案。

    【check方法】

    找到一段排名连续的[l, r]都满足height[i]>=mid( l<i<=r )的连续的区间,对该区间[l, r]进行处理,判断所有的sa[i]( l<=i<=r )的所属Mi,如果有N个不同的Mi即满足条件,返回true,

    反之返回false。

    上代码

    #include <bits/stdc++.h>
    using namespace std;
    
    const int maxn = 1e6 + 5;
    int n, t, M, b[maxn], num[110], a[maxn];
    int sa[maxn], x[maxn], c[maxn], y[maxn], rk[maxn], height[maxn];
    bool vis[maxn];
    inline void get_sa(){
        int m = 2000;
        for( int i=1; i<=n; i++ ) ++c[x[i]=a[i]];
        for( int i=1; i<=m; i++ ) c[i] += c[i-1];
        for( int i=n; i; i-- ) sa[c[x[i]]--] = i;
        for( int k=1; k<=n; k<<=1 ){
            int now = 0;
            for( int i=n-k+1; i<=n; i++ ) y[++now] = i;
            for( int i=1; i<=n; i++ ) if(sa[i]>k) y[++now] = sa[i]-k;
            for( int i=0; i<=m; i++ ) c[i] = 0;
            for( int i=1; i<=n; i++ ) ++c[x[i]];
            for( int i=1; i<=m; i++ ) c[i] += c[i-1];
            for( int i=n; i; i-- ) sa[c[x[y[i]]]--] = y[i], y[i]=0;
            swap(x, y);
            x[sa[1]] = now = 1;
            for( int i=2; i<=n; i++ )
                x[sa[i]] = (y[sa[i]]==y[sa[i-1]] && y[sa[i]+k]==y[sa[i-1]+k]) ? now : ++now;
            if( now>=n ) return ;
            m = now;
        }
    }
    
    inline void get_height(){
        int k = 0;
        for( int i=1; i<=n; i++ ) rk[sa[i]] = i;
        for( int i=1; i<=n; i++ ){
            if( rk[i]==1 ) continue;
            if( k ) k--;
            int j = sa[rk[i]-1];
            while( j+k<=n && i+k<=n && a[j+k]==a[i+k] ) k++;
            height[rk[i]] = k;
        }
    }
    
    inline bool check1( int l, int r ){
        if( r-l+1<t ) return 0;                 //!!!!!!!r-l+1<t  不是r-l+1<n
        int tmp = 0;
        memset( vis, 0, sizeof(vis) );
        for( int i=l; i<=r; i++ )
            if( !vis[b[sa[i]]] ){
                tmp ++;
                vis[b[sa[i]]] = 1;
            }
        return tmp == t;
    }
    
    inline bool check( int x ){
        int l=1, r=1;
        while( l<=n ){
            while( height[r+1]>=x ) r++;
            if( check1(l, r) ) return 1;
            l = r+1; r = l;
        }
        return 0;
    }
    
    int main(){
        freopen("in.txt", "r", stdin);
        scanf("%d", &t);
        int len = 0;
        for( int i=1; i<=t; i++ ){
            scanf("%d", &M);
            for( int j=0; j<M; j++ ) scanf("%d", &num[j]);
            for( int j=1; j<M; j++ ){
                a[++n] = num[j]-num[j-1];           //进行差分
                b[n] = i;
            }
            a[++n] = 0;         //分隔不同的串
            b[n] = i;
        }
        // for( int i=1; i<=n; i++ ) cout << a[i] << endl;
        get_sa();
        get_height();
        // for( int i=1; i<=n; i++ ) cout << height[i] <<endl;
        int l=1, r=n;
        int 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+1);
    
         return 0;
    }
  • 相关阅读:
    cjson库的使用以及源码阅读
    Map集合 把map 集合 转成Set的方法
    JAVA 吃货联盟
    第二本 第六章 接口 采用面向接口编程组装一台计算机
    java 类和对象
    Jdk安装和环境配置
    Spring mvc 同类之间方法的互相跳转 "redirect:/manage/ManageUser";
    关于Mybatis参数传值问题(常用) 个人比较推荐第二种哦,可以减少代码量,唯一要注意的是自己传递的参数个数个顺序就好
    数据库拼接字符模糊查询语句(mybatis中运用(xml文件))
    ExpandableListAdapter实现的三程常用方式
  • 原文地址:https://www.cnblogs.com/WAautomaton/p/11563306.html
Copyright © 2020-2023  润新知