8.4日字符串专题模拟赛
今天模拟赛T4数据锅了\所以全场没分
T2T3的数据极其强大
链接:
[ZJOI2009]对称的正方形
大致题意:给出一个矩形,求出所有的上下左右对称的正方形子矩阵个数(回文正方形)
考场就没细看,直接跳了
大概是二维马拉车,我们在处理时要上下求回文
[POI2012]OKR-A Horrible Poem
大致题意:给定一个字符串,然后有 m 次询问,每次给出一个区间 [ l , r ] ,求出这个区间子串最短循环节。
最简单的想法就是将一个子串拆开,拆成 a + b , b + a 的样子(感性理解)
也就是找到最小的len使得 hash(l+len,r) == hash(l,r-len)
然后我们去枚质因子,加点卡常技巧就过了、
#include<bits/stdc++.h> #define int long long using namespace std; const int N=5e5+7; const int mod=1e9+7; int n,m; char a[N]; int h[N],mul[N],g[N]; bool vis[N]; vector<int >pri; inline int read() { register int x=0,f=1;register char c=getchar(); while(c<'0'||c>'9') { if(c=='-') f=-1; c=getchar(); } while(c>='0'&&c<='9') { x=x*10+c-'0'; c=getchar(); } return x*f; } inline void euler() { for(register int i=2;i<=n;i++) { if(!vis[i]) pri.push_back(i),g[i]=i; for (int j=0;j<pri.size()&&pri[j]*i<=n;j++) { vis[pri[j]*i]=true,g[pri[j]*i]=pri[j]; if (i%pri[j]==0) break; } } return ; } inline int get(int l,int r) { return ((h[r]-h[l-1]*mul[r-l+1])%mod+mod)%mod; } signed main() { n=read(); for(register int i=1;i<=n;i++) cin>>a[i]; euler(); mul[0]=1; for(register int i=1;i<=n;i++) { mul[i]=mul[i-1]*233%mod; h[i]=(h[i-1]*233+a[i]-'a'+1)%mod; } cin>>m; while(m--) { register int x,y; x=read(),y=read(); register int L=y-x+1; register int ans=L; if(get(x+1,y)==get(x,y-1)) { cout<<1<<endl; continue; } while(L-1) { if(get(x+ans/g[L],y)==get(x,y-ans/g[L])) ans/=g[L]; L/=g[L]; } cout<<ans<<endl; } return 0; }
[POI2012]PRE-Prefixuffix
大致题意:寻找最大前缀等于后缀
Hash + 推理
1.L < = n/2
2.我们可以手玩一个循环同构
比如(样例):
ab abba
abba ab
由于是从原本的串里挑前后缀串,所以我们考虑分别从头尾枚(删)
可以设:
line[i]为删去i个字符后剩余的最长相等前后缀
可以发现单调关系:
line[i+1]+2 ≥ line[i]
相当于我们每往后考虑一个最多会比当前的最长前后缀长度少2(由1得)
自此我们可以倒序枚举 i ,来 O( n )求出line数组
然后大力一下即可
又由于此题数据原因:我们考虑双hash
#include<bits/stdc++.h> #define int long long using namespace std; const int N=1e6+7; const int mod=1e9+7; int n; char a[N]; int head[N],tail[N],mulh[N],mult[N]; int ans=0; int line[N];//前缀去掉i个,剩余前后缀相等的 bool pd(int x,int y, int l) { if( (((int)((int)(head[x+l-1]-head[x-1]*mulh[l])%mod+mod)%mod)==((int)((int)(head[y+l-1]-head[y-1]*mulh[l])%mod+mod)%mod) ) && (((int)((int)(tail[x+l-1]-tail[x-1]*mult[l])%mod+mod)%mod)==((int)((int)(tail[y+l-1]-tail[y-1]*mult[l])%mod+mod)%mod)) ) return 1; else return 0; } signed main() { ios::sync_with_stdio(false); cin>>n; for(int i=1;i<=n;i++) cin>>a[i]; mulh[0]=1; mult[0]=1; for(int i=1;i<=n;i++) { mulh[i]=mulh[i-1]*666%mod; mult[i]=mult[i-1]*2333%mod; head[i]=(head[i-1]*666+(a[i]-'a'+1))%mod; tail[i]=(tail[i-1]*2333+(a[i]-'a'+1))%mod; } for(int L=n/2;L>=1;L--) { line[L]=min((n-2*L)/2,line[L+1]+2); while(!pd(L+1,n-line[L]+1-L,line[L])) if(line[L]) line[L]--; } for(int L=1;L<=n/2;L++) { if(pd(1,n-L+1,L)) ans=max(ans,line[L]+L); } cout<<ans; return 0; }
[USACO15FEB]Censoring G
大致题意:给定一个字符串,有m个我们需要在原字符串删除的单词。
不难从题意中得出:这是个AC自动机,题意很明确
那么... ...我们应该如何进行删除操作呢。。。
我们可以开两个栈,一个用于记录合法字符,另一个用于记录我们的AC自动机进程
当匹配到删除的单词,我们将两个栈同时删去单词。
每次放入被匹配字符串的一个字符。如果当前栈中字符数量大于等于匹配串的长度,开始匹配,如果有一个单词匹配失败,break掉,继续放字符。
#include<bits/stdc++.h> using namespace std; const int N=1e5+7; int n; struct node { int son[30]; int fail,cnt; }a[N]; char s[N],c[N],st[N]; queue<int >q; int g[N]; int _; void init(int len) { int x=0; for(int i=1;i<=len;i++) { if(a[x].son[c[i]-'a']) x=a[x].son[c[i]-'a']; else { _++; x=a[x].son[c[i]-'a']=_; } } a[x].cnt=len; return ; } void bfs() { int x=0; for(int i=0;i<=25;i++) { if(a[0].son[i]) q.push(a[0].son[i]); } while(!q.empty()) { int x=q.front(); q.pop(); for(int i=0;i<=25;i++) { if(a[x].son[i]) { a[a[x].son[i]].fail=a[a[x].fail].son[i]; q.push(a[x].son[i]); } else { a[x].son[i]=a[a[x].fail].son[i]; } } } return ; } int main() { ios::sync_with_stdio(false); cin>>s+1; cin>>n; for(int i=1;i<=n;i++) { cin>>c+1; int len=strlen(c+1); init(len); } int len=strlen(s+1); bfs(); int now=0,num=0; for(int i=1;i<=len;i++) { now=g[num]; num++; st[num]=s[i]; now=a[now].son[s[i]-'a']; g[num]=now; if(a[now].cnt) { for(int j=num-a[now].cnt+1;j<=num;j++) { st[j]=0; g[j]=0; num-=a[now].cnt; } } } for(int i=1;i<=num;i++) cout<<st[i]; return 0; } /* begintheescapexecutionatthebreakofdawn 2 escape execution */