zoukankan      html  css  js  c++  java
  • 2016 ACM-ICPC China Finals #F Mr. Panda and Fantastic Beasts

    题目链接$ ewcommand{LCP}{mathrm{LCP}} ewcommand{suf}{mathrm{suf}}$

    题意

    给定 $n$ 个字符串 $s_1, s_2, dots, s_n$,求只在 $s_1$ 中出现过的最短子串,若有多解,输出字典序最小的。

    分析

    为了方便, 称只在 $s_1$ 中出现过的子串为「特殊子串」,记「字符串 $s$ 是字符串 $t$ 的子串」作 $ s sqsubseteq t$ 。

    引理 1
    若 $s'$ 是特殊子串,若字符串 $s$ 满足 $s sqsubseteq s_1$ 且 $s' sqsubseteq s$,那么 $s$ 也是特殊子串。

    因此可二分答案。

    问题化为: 判断是否存在长为 $x~(x le |s_1|)$ 的特殊子串.

    用后缀数组解决上述判定问题

    1. 将 $s_1, s_2, dots, s_n$ 用 未在原串中出现过各不相同 的字符链接成一个长串 $s$ , 构造 $s$ 的后缀数组 $suf_1, suf_2, dots, suf_i, dots$ 和 height 数组 $h_1, h_2, dots, h_i, dots$ .
    2. 寻找后缀数组中满足如下条件的连续段 $suf_i, suf_{i+1}, dots, suf_j, dots, suf_{i+k-1}$.
      1. $suf_j$ 起始于 $s_1$ 中
      2. $|suf_j sqcap s_1| ge x$
      3. $|LCP(suf_{j_1}, suf_{j_2})| ge x$, $LCP(s, t)$ 指字符串 $s, t$ 的最长公共前缀 (Longest Common Prefix)
      4. $h_i<x$ 且 $h_{i+k}<x$

    存在长为 $x$ 的特殊子串 $Longleftrightarrow$ 存在上述连续段

    Implementation

    #include <bits/stdc++.h>
    using namespace std;
    
    const int N=3e5+5;
    char s[N];
    int a[N];
    int suf[N], suf2[N], rk[N], cnt[N], h[N];
    
    void SA(int *s, int n, int m, int *suf, int *suf2, int *rk, int *cnt){
        for(int i=0; i<m; i++)
            cnt[i]=0;
        for(int i=0; i<n; i++)
            cnt[rk[i]=s[i]]++;
        for(int i=1; i<m; i++)
            cnt[i]+=cnt[i-1];
        for(int i=n-1; i>=0; i--)
            suf[--cnt[rk[i]]]=i;
    
        for(int len=1; len<n; len<<=1){
            int tail=0;
            for(int i=n-len; i<n; i++)
                suf2[tail++]=i;
            for(int i=0; i<n; i++)
                if(suf[i]>=len) suf2[tail++]=suf[i]-len;
    
            for(int i=0; i<m; i++)
                cnt[i]=0;
            for(int i=0; i<n; i++)
                cnt[rk[i]]++;
            for(int i=1; i<m; i++)
                cnt[i]+=cnt[i-1];
            for(int i=n-1; i>=0; i--)
                suf[--cnt[rk[suf2[i]]]]=suf2[i];
    
            swap(suf2, rk);
            auto same=[suf2, len](int i, int j){
                return suf2[i]==suf2[j] && suf2[i+len]==suf2[j+len];
            };
    
            rk[suf[0]]=0;
            for(int i=1; i<n; i++)
                rk[suf[i]]=rk[suf[i-1]]+!same(suf[i], suf[i-1]);
    
            m=rk[suf[n-1]]+1;
            if(m==n) break;
        }
    }
    
    void calc(int *a, int *suf, int n, int *rk, int *h){
        for(int i=0; i<n; i++)
            rk[suf[i]]=i;
    
        for(int i=0, lcp=0; i<n-1; i++){
            if(lcp) --lcp;
            for(int j=suf[rk[i]-1]; a[j+lcp]==a[i+lcp]; lcp++);
            h[rk[i]]=lcp;
        }
    }
    
    int ok(int x, int len0, int len, int *h){
        //two-pointers
        for(int i=1; i<=len; i++){  //tricky
            if(suf[i]<len0 && suf[i]+x <= len0 && h[i]<x){  //error-prone
                for(++i; i<=len && h[i]>=x && suf[i]<len0; i++);
                if(i>len || h[i]<x){
                    assert(suf[i-1]>=0);
                    return suf[i-1];
                }
            }
        }
        return -1;
    }
    
    int main(){
        int T;
        cin>>T;
    
        for(int cas=1; cas<=T; cas++){
            printf("Case #%d: ", cas);
            int n;
            cin>>n;
            cin>>s;
            int len0=strlen(s), len=len0;
    
            for(int i=1; i<n; i++){
                s[len++]='#';
                cin>>(s+len);
                len+=strlen(s+len);
            }
    
            int m=256;
    
            for(int i=0; i<=len; i++){
                if(s[i]=='#') a[i]=m++;
                else a[i]=s[i];
            }
    
            SA(a, len+1, m, suf, suf2, rk, cnt);
            calc(a, suf, len+1, rk, h);
    
    
            int l=0, r=len0+1, res=-1;
            for(; l+1<r; ){
                int mid=(l+r)>>1;
                int tmp=ok(mid, len0, len, h);
                if(tmp!=-1){
                    res=tmp;
                    r=mid;
                }
                else l=mid;
            }
    
            if(r>len0) puts("Impossible");
            else{
                for(int i=res; i<res+r; i++)
                    putchar(s[i]);
                puts("");
            }
        }
    
        return 0;
    }
    
  • 相关阅读:
    11.【nuxt起步】-登录验证
    10.【nuxt起步】-引用mintui
    9.【nuxt起步】-scroll分页加载
    8.【nuxt起步】-vue组件之间数据交互
    7.【nuxt起步】-Nuxt与后端数据交互
    6.【nuxt起步】-完成一个静态的页面
    5.【nuxt起步】-swiper组件
    4.【nuxt起步】-具体练习一个h5实例
    3.【nuxt起步】-下面以一个SPA单页程序为例子
    2.【nuxt起步】-初始化创建nuxt项目
  • 原文地址:https://www.cnblogs.com/Patt/p/6354086.html
Copyright © 2011-2022 走看看