• [atARC141F]Welldefined Abbreviation


    取$T=S_{i}$,不断删除其任意子串$\in \{S_{j}\mid i\ne j\}$,最终应有$T\in \{S_{i},\empty\}$

    关于该过程的实现,考虑建立AC自动机,并从前往后依次加入字符

    若当前节点存在后缀为某子串,则删去该后缀,并跳到剩余部分对应位置

    若$T=\empty$,则删除$S_{i}$可以以该过程代替,不妨去掉$S_{i}$,且这并不影响其余串

    在此基础上,对于剩下的这些串(最终$T=S_{i}$),即两两不成子串关系

    结论:$\exists T\iff \exists A,B,C$(其中$A\ne C$且均非空)满足$AB,BC\in \{S_{i}\}$

    关于充分性,取$T=ABC$即可(注意到$A$和$C$均不存在$\{S_{i}\}$中的子串)

    关于必要性,若满足此条件,对$|T|$从小到大归纳,并取出$T$中所有$\in \{S_{i}\}$的子串

    注意到对应位置两两不交(不成子串关系&不存在$A,B,C$),进而可以将这些全部删除

    具体的,记$T_{i}$表示删去第$i$个串,$T_{0}$表示全部删除,$f(T)$表示$T$所有可能的结果并

    显然$f(T_{0})\subseteq f(T_{i})$,根据归纳两者大小均为$1$,进而$f(T)=\bigcup f(T_{i})=f(T_{0})$,即得证

    关于该条件的判定,枚举其中$AB$对应串即后缀(借助$fail$指针)

    若对应子树内存在多个串,显然总存在$C\ne A$,否则哈希比较两者即可

    时间复杂度为$o(\sum |s_{i}|)$,可以通过

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 #define N 2000005
     4 #define mod 998244353
     5 #define base 47
     6 #define ll long long
     7 int n,V,l,k,dep[N],ed[N],nex[N],pos[N],vis[N],cnt[N],ch[N][4];
     8 char s[N];queue<int>q;vector<int>v[N];
     9 void init(){
    10     V=1;
    11     memset(ch,0,sizeof(ch));
    12 }
    13 void add(int id){
    14     k=1;
    15     for(int c:v[id]){
    16         if (!ch[k][c])ch[k][c]=++V,dep[V]=dep[k]+1;
    17         k=ch[k][c];
    18     }
    19 }
    20 void build(){
    21     for(int i=0;i<4;i++){
    22         if (!ch[1][i])ch[1][i]=1;
    23         else nex[ch[1][i]]=1,q.push(ch[1][i]);
    24     }
    25     while (!q.empty()){
    26         int k=q.front();q.pop();
    27         if (!ed[k])ed[k]=ed[nex[k]];
    28         for(int i=0;i<4;i++){
    29             if (!ch[k][i])ch[k][i]=ch[nex[k]][i];
    30             else nex[ch[k][i]]=ch[nex[k]][i],q.push(ch[k][i]);
    31         }
    32     } 
    33 }
    34 int main(){
    35     scanf("%d",&n),init();
    36     for(int i=1;i<=n;i++){
    37         scanf("%s",s),l=strlen(s);
    38         for(int j=0;j<l;j++)v[i].push_back(s[j]-'A');
    39         add(i),ed[k]=l;
    40     }
    41     build();
    42     for(int i=1;i<=n;i++){
    43         l=0,k=pos[0]=1;
    44         for(int c:v[i]){
    45             k=ch[k][c];
    46             if ((!ed[k])||(ed[k]==v[i].size()))pos[++l]=k;
    47             else l-=ed[k]-1,k=pos[l];
    48         }
    49         if ((l)&&(l<v[i].size())){
    50             printf("Yes\n");
    51             return 0;
    52         }
    53         vis[i]=(l==v[i].size());
    54     }
    55     init();
    56     for(int i=1;i<=n;i++)
    57         if (vis[i])add(i);
    58     build();
    59     for(int i=1;i<=n;i++)
    60         if (vis[i]){
    61             l=0,k=pos[0]=1;
    62             for(int c:v[i])k=pos[++l]=ch[k][c],cnt[k]++;
    63             ed[pos[l]]=0;
    64             for(l--;l;l--)ed[pos[l]]=((ll)base*ed[pos[l+1]]+v[i][l]+1)%mod;
    65         }
    66     for(int i=1;i<=n;i++)
    67         if (vis[i]){
    68             int s=1;l=pos[0]=0,k=1;
    69             for(int c:v[i]){
    70                 l++,k=ch[k][c];
    71                 pos[l]=(pos[l-1]+(ll)s*(c+1))%mod,s=(ll)base*s%mod;
    72             }
    73             for(k=nex[k];k>1;k=nex[k])
    74                 if ((cnt[k]>1)||(ed[k]!=pos[l-dep[k]])){
    75                     printf("Yes\n");
    76                     return 0;
    77                 }
    78         }
    79     printf("No\n");
    80     return 0;
    81 } 
    View Code
  • 相关阅读:
    javascript练习:87设置对象事件的方法
    javascript练习:810事件与this运算符
    SQL: case when的用法(转)
    C++ :stringstream介绍,使用方法与例子(转)
    【玩转.Net MF – 01】Flash远程读写
    【.Net Micro Framework PortingKit – 13】LCD驱动开发
    【玩转.Net MF – 03】远程文件查看器
    【玩转.Net MF – 02】让PC成为MF的鼠标键盘
    【.Net Micro Framework PortingKit – 12】SysTick驱动开发
    【.Net Micro Framework PortingKit – 11】NandFlash驱动开发
  • 原文地址:https://www.cnblogs.com/PYWBKTDA/p/16486915.html
Copyright © 2020-2023  润新知