zoukankan      html  css  js  c++  java
  • 一些字符串有关的题目

    模板可以在上一篇文章中找到。

    因为最近都没有做codeforces,所以这篇文章的主要题目来源就是codeforces啦~

    需要这类题目可以在codeforces上找到hashing、string suffix structures之类的标签。

    这些题目都是随便点的,所以有些题目和字符串并没有太大的关系

    CF653F Paper Task(非常规比赛)

    给一个长度为n的由左右括号做成的字符串,求它子串中不同括号序列的个数。

    (注意不是求是合法括号序列的子串数量,而是不同括号序列个数)

    1<=n<=500000。

    官方题解 http://codeforces.com/blog/entry/43886

    这里讲一下官方解法1(话说官方题解实在厉害啊...省略了大量细节)

    我们用query(l,r)表示序列的l~r这些有多少个前缀是合法括号序列。

    这玩意儿要怎么求呢?我们把左括号当做+1,右括号当做-1,那么我们就是要找一个左端点为l,右端点在[l,r]的区间使得和为0,并且右端点在那之前的区间和都不能为负(为了保证不会出现类似))((的情况)。

    那么我们处理出前缀和,然后我们考虑用一个单调队列搞一搞,我们就可以搞出对于每一个qzh[x],后面小于它的第一个qzh。假设小于qzh[l-1]的第一个为qzh[p],那么到了这里我们就要询问[l,min(p-1,r)]有多少个qzh等于qzh[l-1]的元素。

    我们可以用sort+二分简单地搞定这个问题,把qzh当做pair<int,int> sort一下,然后再二分一发就可以了。

    现在我们在O(nlogn)预处理+O(logn)询问的时间内搞定了query。

    接下来我们发现对于每一个后缀s只要建后缀数组求出height,将query(s,n)-query(s,s+height[s]-1)计入答案就行了(减去那玩意儿是为了扣掉上一个后缀算过的答案)。

    那么这题就做完了~大概是O(nlogn),不管是不是cf,3s都应该是可以过的。

    #include <iostream>
    #include <stdio.h>
    #include <stdlib.h>
    #include <algorithm>
    #include <string.h>
    #include <vector>
    #include <limits>
    #include <set>
    #include <map>
    using namespace std;
    #define SZ 666666
    int n,k,sa[SZ],t[SZ],rank[SZ],qzh_[SZ],tmpsa[SZ],tmpr[SZ],h[SZ];
    char s[SZ];
    bool same(int a,int b,int p) {return t[a]==t[b]&&t[a+p]==t[b+p];}
    void getsa(int m=500)
    {
        s[++n]=0;
        for(int i=0;i<n;i++) rank[i]=s[i], ++qzh_[rank[i]];
        for(int i=1;i<m;i++) qzh_[i]+=qzh_[i-1];
        for(int i=n-1;i>=0;i--) sa[--qzh_[rank[i]]]=i;
        for(int j=1;j<=n;j<<=1)
        {
            int cur=-1;
            for(int i=n-j;i<n;i++) tmpsa[++cur]=i;
            for(int i=0;i<n;i++) if(sa[i]>=j) tmpsa[++cur]=sa[i]-j;
            for(int i=0;i<n;i++) tmpr[i]=rank[tmpsa[i]];
            for(int i=0;i<m;i++) qzh_[i]=0;
            for(int i=0;i<n;i++) ++qzh_[tmpr[i]];
            for(int i=1;i<m;i++) qzh_[i]+=qzh_[i-1];
            for(int i=n-1;i>=0;i--) t[i]=rank[i], sa[--qzh_[tmpr[i]]]=tmpsa[i];
            m=0;
            for(int i=0;i<n;i++)
                rank[sa[i]]=(i>0&&same(sa[i],sa[i-1],j))?m:++m;
            ++m;
        }
        for(int i=0;i<n;i++) rank[sa[i]]=i;
        int p=0;
        for(int i=0;i<n;i++)
        {
            if(p) --p;
            int ls=sa[rank[i]-1];
            while(s[ls+p]==s[i+p]) p++;
            h[rank[i]]=p;
        }
        --n;
        for(int i=1;i<=n;i++) sa[i-1]=sa[i];
        for(int i=0;i<n;i++) rank[sa[i]]=i;
        for(int i=2;i<=n;i++) h[i-1]=h[i];
        h[n]=sa[n]=h[0]=0;
    }
    int qzh[SZ],dy[2333],gs[SZ],pos[SZ];
    int ss[SZ],sn=0;
    typedef pair<int,int> pii;
    pii qss[SZ];
    int query(int l,int r)
    {
        if(l>r) return 0;
        return upper_bound(qss,qss+1+n,pii(qzh[l],min(gs[l]-1,r+1)))-qss-1-pos[l];
    }
    int main()
    {
        dy['(']=1; dy[')']=-1;
        scanf("%*d%s",s);
        n=strlen(s); getsa();
        for(int i=1;i<=n;i++) qzh[i]=qzh[i-1]+dy[s[i-1]];
        for(int i=n;i>=0;i--)
        {
            int x=qzh[i];
            while(sn&&qzh[ss[sn]]>=x) --sn;
            if(!sn) gs[i]=n+1;
            else gs[i]=ss[sn];
            ss[++sn]=i;
        }
        for(int i=0;i<=n;i++) qss[i]=pii(qzh[i],i);
        sort(qss,qss+1+n);
        for(int i=0;i<=n;i++) pos[qss[i].second]=i;
        long long ans=0;
        for(int i=0;i<n;i++)
        {
            ans+=query(sa[i],n-1);
            ans-=query(sa[i],sa[i]+h[i]-1);
        }
        printf("%I64d
    ",ans);
    }

    CF631D Messenger(Div2 D)

    给定两个压缩过的字符串s和t,求s在t中的出现次数。

    压缩方式:3-a 2-b展开为aaabb,类似这样。

    如果我们把3-a、2-b这种东西叫做压缩节,那么保证每个字符串的压缩节个数<=200000。保证每个压缩节的前面那个玩意儿(展开次数)<=1000000。

    官方题解 http://codeforces.com/blog/entry/43551

    首先我们应该把压缩节“化简”一下以便于下面的处理。这里指把1-a 1-a化成2-a这样子。

    当s的压缩节<=2时可以特判。

    其它情况下我们需要找到一对(l,r)使得s[l+1...r-1]=t[2...|t|-1]并且左边比较靠谱,右边也比较靠谱,我们就可以把t[2...|t|-1]扔去和s进行kmp,对于每一处匹配暴力算一算是否满足。

    坑点:虽然看起来输入里的展开次数<=1000000,可是你一合并就爆int了...

    #include <iostream>
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <algorithm>
    using namespace std;
    #define SZ 666666
    int n,m;
    typedef pair<long long,char> pic;
    pic as[SZ],bs[SZ];
    #define f_ first
    #define s_ second
    void m_1()
    {
        long long ans=0;
        for(int i=1;i<=n;i++)
        {
            if(as[i].s_==bs[1].s_&&as[i].f_>=bs[1].f_) ans+=as[i].f_-bs[1].f_+1;
        }
        printf("%I64d
    ",ans);
    }
    void m_2()
    {
        long long ans=0;
        for(int i=1;i+1<=n;i++)
        {
            if(as[i].s_==bs[1].s_&&as[i].f_>=bs[1].f_&&as[i+1].s_==bs[2].s_&&as[i+1].f_>=bs[2].f_) ++ans;
        }
        printf("%I64d
    ",ans);
    }
    int ms=0,ml[SZ],mr[SZ];
    struct HashKMP
    {
    pic s[SZ+1];
    int n,next[SZ+3];
    void gnext()
    {
        n=0;
        while(s[n].s_) ++n;
        next[0]=-1;
        int j=-1;
        for(int i=1;i<n;i++)
        {
            while(j!=-1&&s[i].s_!=s[j+1].s_) j=next[j];
            if(s[i].s_==s[j+1].s_) ++j;
            next[i]=j;
        }
    }
    void kmp(pic* a)
    {
        int j=-1;
        for(int i=0;a[i].s_;i++)
        {
            while(j!=-1&&s[j+1]!=a[i]) j=next[j];
            if(s[j+1]==a[i]) ++j;
            if(j==n-1) ++ms, ml[ms]=i-n+1, mr[ms]=i+2;
        }
    }
    }ha;
    pic hb[SZ];
    int main()
    {
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++)
        {
            int a; char b[3];
            scanf("%d-%s",&a,b);
            if(i>1&&as[i-1].s_==b[0])
            {
                as[i-1].f_+=a; --i; --n; continue;
            }
            as[i]=pic(a,b[0]);
        }
        for(int i=1;i<=m;i++)
        {
            int a; char b[3];
            scanf("%d-%s",&a,b);
            if(i>1&&bs[i-1].s_==b[0])
            {
                bs[i-1].f_+=a; --i; --m; continue;
            }
            bs[i]=pic(a,b[0]);
        }
        if(m==1) {m_1(); return 0;}
        if(m==2) {m_2(); return 0;}
        for(int i=2;i<=m-1;i++) ha.s[i-2]=bs[i];
        ha.gnext();
        for(int i=1;i<=n;i++) hb[i-1]=as[i];
        ha.kmp(hb);
        long long ans=0;
        for(int i=1;i<=ms;i++)
        {
            int a=ml[i],b=mr[i];
            if(a<1||b<1||a>n||b>n) continue;
            if(as[a].s_==bs[1].s_&&as[a].f_>=bs[1].f_);else continue;
            if(as[b].s_==bs[m].s_&&as[b].f_>=bs[m].f_);else continue;
            ++ans;
        }
        printf("%I64d
    ",ans);
    }

    CF526D Om Nom and Necklace(非常规比赛)

    我们叫一个串S为常规串当且仅当存在两个串A、B(均可为空)使得S=A+B+A+B+...+A。

    其中加号表示字符串连接,需要注意的是串需要以A开头与结尾,并且需要有k+1个A与k个B,k为一个给定数。

    找到一个串S的哪些前缀是常规串,按01输出。

    1<=n,k<=1000000。

    官方题解 http://codeforces.com/blog/entry/17281

    对于一个前缀P,我们可以把它分割为P=SSS....SST,其中T是S的一个前缀,并且S尽量短。这个玩意儿可以用kmp来做。

    注意到kmp中的next[j]应该是所有满足B[1..next[j]]=B[j-next[j]+1..j]的最大值。那么可以发现只要令|S|=j-next[j]就符合条件。

    为了和谐我们应该要让A+B尽量长,那么A+B就要包含(S的个数/K)个S,A就要是(S的个数%K)个S+T。我们只要检查一下是否满足|A+B|>=|A|就行了。

    image

    #include <iostream>
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <time.h>
    #include <algorithm>
    using namespace std;
    #define SZ 2333333
    int k;
    struct HashKMP
    {
    char s[SZ+1]; int n;
    int next[SZ+3];
    void gnext()
    {
        n=strlen(s);
        next[0]=-1;
        int j=-1;
        for(int i=1;s[i];i++)
        {
            while(j!=-1&&s[i]!=s[j+1]) j=next[j];
            if(s[i]==s[j+1]) ++j;
            next[i]=j;
        }
        for(int i=0;s[i];i++)
        {
            int len_s=i-next[i],len_t=(i+1)%len_s;
            int cnt_s=(i+1-len_t)/len_s;
            if(cnt_s/k*len_s>=cnt_s%k*len_s+len_t) putchar('1');
            else putchar('0');
        }
    }
    }ha;
    int main()
    {
        scanf("%*d%d%s",&k,ha.s);
        ha.gnext();
    }

    CF557E Ann and Half-Palindrome(Div2 E)

    我们把一个串t称为半循环串,当且仅当对于每一个奇数的i(image)均满足image

    现在给定一个由a、b两个字母组成的串s,求将s的所有子串中半循环串按字典序从小到大排序之后第k大的半循环串。

    1<=|s|<=5000。

    官方题解 http://codeforces.com/blog/entry/18943

    注意到(因为是cf)这题是允许一个平方做法的。

    我们可以dp出任意一个区间是否是半循环串。用good[i][j]表示i~j是否是半循环串,如果s[i]!=s[j]那么显然good[i][j]=0,否则good[i][j]可以用good[l+2][r-2]来更新。

    接下来我们把所有后缀插入trie,并且统计出每一棵子树内半循环串的数量。因为是二叉的,所以可以像splay那样向左向右走来获取答案。

    #include <iostream>
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <algorithm>
    using namespace std;
    #define SZ 5010
    #define SZZ 25000100
    char s[SZ+1]; int n,k;
    bool good[SZ][SZ];
    int ch[SZZ][2],val[SZZ],sum[SZZ],M=1;
    int alc(int& x)
    {
        if(!x) x=++M;
        return x;
    }
    int main()
    {
        scanf("%s%d",s+1,&k);
        n=strlen(s+1);
        for(int l=1;l<=n;l++)
        {
            for(int i=1;i+l-1<=n;i++)
            {
                int j=i+l-1;
                if(s[i]!=s[j]) continue;
                if(i+2<=j-2&&!good[i+2][j-2]) continue;
                good[i][j]=1;
            }
        }
        for(int i=1;i<=n;i++)
        {
            int cur=1;
            for(int j=i;j<=n;j++)
            {
                cur=alc(ch[cur][s[j]-'a']);
                val[cur]+=good[i][j];
            }
        }
        for(int i=M;i>=1;i--) sum[i]=sum[ch[i][0]]+sum[ch[i][1]]+val[i];
        int cur=1; string s;
        while(k>val[cur])
        {
            k-=val[cur];
            if(sum[ch[cur][0]]>=k) cur=ch[cur][0], s+="a";
            else k-=sum[ch[cur][0]], cur=ch[cur][1], s+="b";
        }
        puts(s.c_str());
    }

    做不下去了...就先做这几题把

  • 相关阅读:
    内存溢出和内存泄漏的区别
    测试管理三要素(人员、过程和技术)
    面试可提问的6个问题
    弱网测试(二)
    js捕获错误
    TortoiseGit自动记住用户名密码的方法
    win7 "com surrogate“ 已停止工作的解决办法
    仿百度图片毛玻璃效果
    毛玻璃效果
    vimium快捷键列表
  • 原文地址:https://www.cnblogs.com/zzqsblog/p/5595326.html
Copyright © 2011-2022 走看看