• BZOJ3620: 似乎在梦中见过的样子


    Description

    “Madoka,不要相信 QB!”伴随着 Homura 的失望地喊叫,Madoka 与 QB 签订了契约.
    这是 Modoka 的一个噩梦,也同时是上个轮回中所发生的事.为了使这一次 Madoka 不再与 QB签订契约,Homura 决定在刚到学校的第一天就解决 QB.然而,QB 也是有许多替身的(但在第八话中的剧情显示它也有可能是无限重生的),不过,意志坚定的 Homura 是不会放弃的——她决定
    消灭所有可能是 QB 的东西.现在,她已感受到附近的状态,并且把它转化为一个长度为 n 的字符串交给了学 OI 的你.
    现在你从她的话中知道 , 所有形似于 A+B+A 的字串都是 QB 或它的替身 , 且len(A)>=k,len(B)>=1 (位置不同其他性质相同的子串算不同子串,位置相同但拆分不同的子串算同一子串),然后你必须尽快告诉 Homura 这个答案——QB 以及它的替身的数量.

    Input

    第一行一个字符串,第二行一个数 k

    Output

    仅一行一个数 ans,表示 QB 以及它的替身的数量

    Sample Input

    【样例输入 1】
    aaaaa
    1
    【样例输入 2】
    abcabcabc
    2

    Sample Output

    【样例输出 1】
    6

    【样例输出 2】
    8

    HINT

    对于 100%的数据:n<=15000 , k<=100,且字符集为所有小写字母

    开始想的是枚举两个位置算LCP,将可行的右端点区间打上标记,时间O(n^2)。

    可惜常数太大。。。。

    //TLE
    #include<cstdio>
    #include<cctype>
    #include<queue>
    #include<cmath>
    #include<cstring>
    #include<algorithm>
    #define lc ch[x][0]
    #define rc ch[x][1]
    #define rep(i,s,t) for(int i=s;i<=t;i++)
    #define dwn(i,s,t) for(int i=s;i>=t;i--)
    #define ren for(int i=first[x];i;i=next[i])
    using namespace std;
    const int BufferSize=1<<16;
    char buffer[BufferSize],*head,*tail;
    inline char Getchar() {
        if(head==tail) {
            int l=fread(buffer,1,BufferSize,stdin);
            tail=(head=buffer)+l;
        }
        return *head++;
    }
    inline int read() {
        int x=0,f=1;char c=getchar();
        for(;!isdigit(c);c=getchar()) if(c=='-') f=-1;
        for(;isdigit(c);c=getchar()) x=x*10+c-'0';
        return x*f;
    }
    typedef long long ll;
    const int maxn=30010;
    int first[maxn],next[maxn],To[maxn],ToT,e;
    void AddEdge(int u,int v) {
        To[++e]=v;next[e]=first[u];first[u]=e;
    }
    int mn[maxn<<1][20],dep[maxn<<1],Log[maxn<<1],Cur,Pos[maxn];
    void dfs(int x,int fa) {
        mn[++Cur][0]=x;Pos[x]=Cur;
        ren if(To[i]!=fa) dep[To[i]]=dep[x]+1,dfs(To[i],x),mn[++Cur][0]=x;
    }
    void init() {
        Log[0]=-1;rep(i,1,Cur) Log[i]=Log[i>>1]+1;
        for(int j=1;(1<<j)<=Cur;j++)
            for(int i=0;i+(1<<j)-1<=Cur;i++) {
                int x=mn[i][j-1],y=mn[i+(1<<j-1)][j-1];
                mn[i][j]=dep[x]<dep[y]?x:y;
            }
    }
    int lca(int x,int y) {
        x=Pos[x];y=Pos[y];if(x>y) swap(x,y);
        int k=Log[y-x+1];x=mn[x][k];y=mn[y-(1<<k)+1][k];
        return dep[x]<dep[y]?x:y;
    }
    int n,m,to[maxn][26],pos[maxn],fa[maxn],l[maxn],cnt=1,last=1;
    void extend(int c,int x) {
        int p=last,q,np,nq;l[last=np=++cnt]=l[p]+1;pos[x]=np;
        for(;!to[p][c];p=fa[p]) to[p][c]=np;
        if(!p) fa[np]=1;
        else {
            q=to[p][c];
            if(l[p]+1==l[q]) fa[np]=q;
            else {
                l[nq=++cnt]=l[p]+1;
                memcpy(to[nq],to[q],sizeof(to[q]));
                fa[nq]=fa[q];fa[q]=fa[np]=nq;
                for(;to[p][c]==q;p=fa[p]) to[p][c]=nq;
            }
        }
    }
    char s[maxn];
    int S[maxn];
    int main() {
        scanf("%s",s+1);int n=strlen(s+1);
        dwn(i,n,1) extend(s[i]-'a',i);
        rep(i,2,cnt) AddEdge(fa[i],i);
        dfs(1,0);init();
        int k=read(),ans=0;
        rep(i,1,n-k-1) {
            rep(j,i+k*2-1,n) S[j]=0;
            rep(j,i+k+1,n) {
                int v=min(l[lca(pos[i],pos[j])],j-i-1);
                if(v>=k) S[k+j-1]++,S[v+j]--;
            }
            rep(j,i+k*2,n) {
                S[j]+=S[j-1];
                if(S[j]) ans++;
            }
        }
        printf("%d
    ",ans);
        return 0;
    }
    View Code

    这道题使用 KMP 做 O(n^2) 的暴力就能过。

    首先,我们依次枚举字串左端点 l ,然后从这个左端点开始向后做一次 KMP。

    然后我们枚举右端点 r ,符合条件的右端点 r 就是 S[l..r] 这一段的一个前缀和后缀相同,并且这一部分的长度 x 要满足 k <= x < (r - l + 1) / 2。

    我们就使用 KMP 的 Next 数组来找这个前缀,直接从 Next[r] 向前找最坏情况会导致变成 O(n^3) ,所以我们应该优化一下:

    Next 数组如果视为 Father 数组,那么它是一棵树。我们求的就是一个节点到根的路径上有没有区间 [k, (r-l+1)/2) 的数。

    我们求 Next 的时候是从根向叶子求的,所以我们可以同时维护根到某个节点 j 的所有数中,大于等于 k 的数中最小的一个。

    然后我们对于每一个右端点,只要判断这个点到根的路径中大于等于 k 的数中最小的一个是否小于 (r - l + 1) / 2 就可以了。

    #include<cstdio>
    #include<cctype>
    #include<queue>
    #include<cmath>
    #include<cstring>
    #include<algorithm>
    #define rep(i,s,t) for(int i=s;i<=t;i++)
    #define dwn(i,s,t) for(int i=s;i>=t;i--)
    #define ren for(int i=first[x];i;i=next[i])
    using namespace std;
    const int BufferSize=1<<16;
    char buffer[BufferSize],*head,*tail;
    inline char Getchar() {
        if(head==tail) {
            int l=fread(buffer,1,BufferSize,stdin);
            tail=(head=buffer)+l;
        }
        return *head++;
    }
    inline int read() {
        int x=0,f=1;char c=getchar();
        for(;!isdigit(c);c=getchar()) if(c=='-') f=-1;
        for(;isdigit(c);c=getchar()) x=x*10+c-'0';
        return x*f;
    }
    const int maxn=15010;
    char s[maxn];
    int n,k,ans,f[maxn],g[maxn];
    int main() {
        scanf("%s",s+1);n=strlen(s+1);k=read();
        rep(i,1,n) {
            f[1]=0;g[0]=1e9;int j=0;
            rep(p,2,n-i+1) {
                while(j&&s[i+j]!=s[i+p-1]) j=f[j];
                if(s[i+j]==s[i+p-1]) j++;f[p]=j;
                if(j<k) g[j]=1e9;
                else g[j]=min(j,g[f[j]]);
                if((g[j]<<1)<p) ans++;
            }
        }
        printf("%d
    ",ans);
        return 0;
    }
    View Code
     
  • 相关阅读:
    reduce()、filter()、map()、some()、every()、...展开属性
    react的this.setState详细介绍
    HDU
    The 2015 ACM-ICPC Asia Beijing Regional Contest
    Ubuntu 14.04 安装 WPScan
    蓝桥杯-历届试题-公式求值
    Ubuntu下快速搭建ACdream Online Judge v1.5.3
    Codeforces Round #290 (Div. 2)
    SOJ
    SOJ
  • 原文地址:https://www.cnblogs.com/wzj-is-a-juruo/p/5050815.html
Copyright © 2020-2023  润新知