标签:
模板可以在上一篇文章中找到。
因为最近都没有做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\n",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\n",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\n",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\n",ans); }
CF633C Spy Syndrome 2(非常规比赛)
有一种对于句子的加密方法是这样的:先将所有字母转成小写,然后把所有单词反序之后拼接在一起。
给定一个加密后的单词和一个包含了一些单词的字典,求任意一个还原方法。
官方题解 http://codeforces.com/blog/entry/43392
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
CF557E Ann and Half-Palindrome(Div2 E)
现在给定一个串s,求将s的所有子串中半循环串按字典序从小到大排序之后第k大的半循环串。
1<=|s|<=5000。
官方题解 http://codeforces.com/blog/entry/18943
CF547E Mike and Friends(感人至深的Div1 E)
有n个人每人有一个字符串作为他们的手机号码。
有一天每一个人要给所有他的号码子串打电话(可能会多次打同一个电话,也会打给自己)。
我们定义call(a,b)为a打给b电话的次数。
1<=n<=200000,1<=q<=500000,字符串总长<=200000。
官方题解 http://codeforces.com/blog/entry/18126
施工中...
标签:
原文地址:http://www.cnblogs.com/zzqsblog/p/5595326.html