其只是内容已经在其他的博客中整理过,这里不再赘述。下面开始例题讲解。
1 例题
1.1 P3454 [POI2007]OSI-Axes of Symmetry
多边形的对称轴数量。我们直接用边长的平方和邻边作为字符,断环为链(复制一段),然后跑 Manacher,判断是否有符合长度的回文串即可。代码:
#include<bits/stdc++.h>
#define dd double
#define ld long double
#define ll long long
#define int long long
#define uint unsigned int
#define ull unsigned long long
#define N 1000100
#define M number
using namespace std;
const int INF=0x3f3f3f3f;
template<typename T> inline void read(T &x) {
x=0; int f=1;
char c=getchar();
for(;!isdigit(c);c=getchar()) if(c == '-') f=-f;
for(;isdigit(c);c=getchar()) x=x*10+c-'0';
x*=f;
}
template<typename T> inline T Max(T a,T b){
return a<b?b:a;
}
template<typename T> inline T Min(T a,T b){
return a<b?a:b;
}
int t,n;
struct point{
ll x,y;
inline point(){}
inline point(ll x,ll y) : x(x),y(y) {}
inline ll operator ^ (const point &b) const{
return x*b.y-y*b.x;
}
inline point operator - (const point &b) const{
return point(x-b.x,y-b.y);
}
};
point a[N];
ll b[N],tail;
ll c[N],L;
inline int dis(point a,point b){
return (a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y);
}
inline void ManacherPre(){
for(int i=1;i<=tail;i++){
c[i*2]=b[i];c[i*2-1]=INF;
}
c[2*tail+1]=INF;L=2*tail+1;
}
int len[N<<2];
inline void Manacher(){
ManacherPre();
int maxright=0,mid=0;
for(int i=1;i<=L;i++){
if(i<maxright) len[i]=Min(len[(mid<<1)-i],len[mid]+mid-i);
else len[i]=1;
while(i+len[i]<=L&&i-len[i]>=1&&c[i+len[i]]==c[i-len[i]]) len[i]++;
if(maxright<i+len[i]){mid=i;maxright=i+len[i];}
}
}
inline void Clear(){
for(int i=1;i<=L;i++) len[i]=c[i]=0;
tail=0;L=0;
}
signed main(){
// freopen("my.in","r",stdin);
// freopen("my.out","w",stdout);
ios::sync_with_stdio(false);cin.tie(0);
cin>>t;
while(t--){
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i].x>>a[i].y;
b[++tail]=((a[n]-a[1])^(a[2]-a[1]));
if(b[tail]>0) b[tail]*=-1;
for(int i=2;i<=n-1;i++){
b[++tail]=dis(a[i-1],a[i]);
b[++tail]=((a[i-1]-a[i])^(a[i+1]-a[i]));
if(b[tail]>0) b[tail]*=-1;
}
b[++tail]=dis(a[n],a[n-1]);
b[++tail]=((a[n-1]-a[n])^(a[1]-a[n]));
if(b[tail]>0) b[tail]*=-1;
b[++tail]=dis(a[1],a[n]);
// for(int i=1;i<=tail;i++) printf("i:%d b:%d\n",i,b[i]);putchar('\n');
for(int i=1;i<=tail;i++) b[i+tail]=b[i];
tail*=2;
// printf("tail:%d\n",tail);
// for(int i=1;i<=tail;i++) printf("i:%d b:%d\n",i,b[i]);putchar('\n');
Manacher();int ans=0;
// for(int i=1;i<=L;i++) printf("i:%d c:%d\n",i,c[i]);putchar('\n');
for(int i=1;i<=L;i++){
int nowlen=len[i]/2;
int now=2*nowlen-1;
if(now>=tail/2){
ans++;
// printf("i:%d\n",i);
}
}
cout<<ans/2<<"\n";
Clear();
}
return 0;
}
/*
update 1 101-106,108 处理循环,计算答案有问题。
*/
1.2 P2375 [NOI2014] 动物园
这个题其实就是 KMP 的 next 数组,但是要求两段不能重叠。一开始我考虑直接在求 next 数组的时候让他们不重叠。但这样是不行的,这是因为你前面求的 next 会影响你求后面的 next 。所以我们直接求出所有的 next,然后统计答案的时候我们相当于是在上次 next 的基础上再求一遍 next,不能一个一个暴力去做,否则会超时。
上面这段话总结一下就是我们先求 next 数组,再求需要满足不重叠的数组。
代码:
#include<bits/stdc++.h>
#define dd double
#define ld long double
#define ll long long
#define int long long
#define uint unsigned int
#define ull unsigned long long
#define N 1000010
#define M number
using namespace std;
const int INF=0x3f3f3f3f;
const ll mod=1e9+7;
template<typename T> inline void read(T &x) {
x=0; int f=1;
char c=getchar();
for(;!isdigit(c);c=getchar()) if(c == '-') f=-f;
for(;isdigit(c);c=getchar()) x=x*10+c-'0';
x*=f;
}
int n,nxt[N],num[N],ans=1;
char s[N];
signed main(){
// freopen("my.in","r",stdin);
// freopen("my.out","w",stdout);
read(n);
while(n--){
ans=1;
scanf("%s",s+1);
int len=strlen(s+1);
nxt[1]=0;num[1]=1;
for(int i=2,j=0;i<=len;i++){
while(j>0&&s[j+1]!=s[i]) j=nxt[j];
if(s[j+1]==s[i]) j++;
nxt[i]=j;num[i]=num[j]+1;
}
// for(int i=1;i<=len;i++){
// int cha=nxt[i]*2-i;
// if(cha>0){nxt[i]-=cha/2;}
// }
for(int i=2,j=0;i<=len;i++){
while(j>0&&s[j+1]!=s[i]) j=nxt[j];
if(s[j+1]==s[i]) j++;
// if(j!=nxt[i]) while(1);
while((j<<1)>i) j=nxt[j];
ans*=(num[j]+1);ans%=mod;
}
// for(int i=1;i<=len;i++){
// if(nxt[i]==0) continue;
// num[i]=(num[nxt[i]]+1)%mod;
// };
// for(int i=1;i<=len;i++) printf("%d ",nxt[i]);putchar('\n');
// for(int i=1;i<=len;i++) printf("%d ",num[i]);putchar('\n');
// for(int i=1;i<=len;i++){
// ans*=(num[i]+1);ans%=mod;
// // printf("nowans:%lld\n",ans);
// }
printf("%lld\n",ans);
for(int i=1;i<=len;i++) num[i]=nxt[i]=0;
}
}
1.3 P3435 [POI2006]OKR-Periods of Words
不难发现如何做这个题,这个也是 KMP 的简单应用,通过在纸上手玩几个数据可以知道,我们相当于不断跳 next 数组,往小了跳,直到不能跳了位置,然后把 \(i-j\) 加上即可。
我们可以用记忆化的方式优化时间复杂度。
代码:
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<bits/stdc++.h>
#include<cstring>
#define dd double
#define ld long double
#define int long long
#define ll long long
#define uint unsigned int
#define ull unsigned long long
#define N 1000010
#define M number
using namespace std;
const int INF=0x3f3f3f3f;
template<typename T> inline void read(T &x) {
x=0; int f=1;
char c=getchar();
for(;!isdigit(c);c=getchar()) if(c == '-') f=-f;
for(;isdigit(c);c=getchar()) x=x*10+c-'0';
x*=f;
}
char s[N];
int nxt[N],ans,n;
signed main(){
read(n);
scanf("%s",s+1);int len=strlen(s+1);
nxt[1]=0;
for(int i=2,j=0;i<=len;i++){
while(j>0&&s[j+1]!=s[i]) j=nxt[j];
if(s[j+1]==s[i]) j++;
nxt[i]=j;
}
int j=0;
for(int i=2;i<=len;i++){
j=i;
while(nxt[j]>0) j=nxt[j];
if(nxt[i]!=0) nxt[i]=j;
ans+=i-j;
}
printf("%lld",ans);
return 0;
}
1.4 UVA10298 Power Strings
我们仍然还是先跑一遍 KMP 求出 next 数组来。然后我们考虑 next 数组的最后一位:\(next_{len}\) ,我们发现用总长度减去这个东西,如果有重复的话,这个长度应该是那个重复的字符串的长度,至于是不是我们只需要判断这个长度是否能整除总长度即可。
#include<bits/stdc++.h>
#define dd double
#define ld long double
#define ll long long
#define uint unsigned int
#define ull unsigned long long
#define N 1000010
#define M number
using namespace std;
const int INF=0x3f3f3f3f;
template<typename T> inline void read(T &x) {
x=0; int f=1;
char c=getchar();
for(;!isdigit(c);c=getchar()) if(c == '-') f=-f;
for(;isdigit(c);c=getchar()) x=x*10+c-'0';
x*=f;
}
char s[N];
int nxt[N],ans;
int main(){
// freopen("my.in","r",stdin);
// freopen("my.out","w",stdout);
int tot=0;
while(1){
tot++;
// printf("here\n");
cin>>(s+1);
int len=strlen(s+1);
if(len==1&&s[1]=='.') break;
nxt[1]=0;
for(int i=2,j=0;i<=len;i++){
while(j>0&&s[j+1]!=s[i]){
j=nxt[j];
}
if(s[j+1]==s[i]) j++;
nxt[i]=j;
}
int ans;
// printf("len:%d\n",len);
// printf("nxt:%d\n",nxt[len]);
if(len%(len-nxt[len])==0) ans=len/(len-nxt[len]);
else ans=len;
printf("%d\n",ans);
}
return 0;
}
1.5 P2603 [ZJOI2008]无序运动
我们先不考虑翻转,先考虑其余的一些变换。不难发现,其余的变换都是相似的,也就是说,对应角相等,对应边成比例。我们考虑如何描述边与角。如果我们直接除的话会有精度,所以每相邻 \(3\) 个点我们用四个整数来表示,前两个整数用来表示边长平方的最简比是多少,后两个整数用来表示点积与叉积的最简比,带符号。然后我们用 AC 自动机跑匹配即可。
为了加快匹配速度,我们可以记一个 \(last_k\) 表示 \(k\) 这个节点一直跳 \(fail\) 跳到的第一个为结尾节点的点是哪一个。
#include<bits/stdc++.h>
#define dd double
#define ld long double
#define ll long long
#define uint unsigned int
#define ull unsigned long long
#define N 200010
#define M number
using namespace std;
const int INF=0x3f3f3f3f;
template<typename T> inline void read(T &x) {
x=0; int f=1;
char c=getchar();
for(;!isdigit(c);c=getchar()) if(c == '-') f=-f;
for(;isdigit(c);c=getchar()) x=x*10+c-'0';
x*=f;
}
inline int gcd(int a,int b){return b==0?a:gcd(b,a%b);}
struct node{
int a,b,c,d;
inline void Print(){printf("%d %d %d %d ",a,b,c,d);}
inline node(){}
inline node(int a,int b,int c,int d) : a(a),b(b),c(c),d(d) {}
inline bool operator < (const node &q) const{
if(a!=q.a) return a<q.a;
else if(b!=q.b) return b<q.b;
else if(c!=q.c) return c<q.c;
else return d<q.d;
}
};
struct Point{
int x,y;
inline Point(){}
inline Point(int x,int y) : x(x),y(y) {}
inline Point operator - (const Point &b) const{return Point(x-b.x,y-b.y);}
inline void Intt(){read(x);read(y);}
};
inline int Dis(Point a,Point b){return (b.x-a.x)*(b.x-a.x)+(b.y-a.y)*(b.y-a.y);}
struct Vector{
int x,y;
inline Vector(){}
inline Vector(int x,int y) : x(x),y(y) {}
inline Vector(Point a) : x(a.x),y(a.y) {}
inline int operator ^ (const Vector &b) const{return x*b.y-y*b.x;}
inline int operator * (const Vector &b) const{return x*b.x+y*b.y;}
};
Point a[N];
node c[N<<2];
int n,m,ans[N],tail;
bool vis[N];
typedef map<node,int>::iterator it;
struct AC_automata{
map<node,int> ch[N];
int tot,fail[N],last[N],cnt[N];
vector<int> end[N];
inline AC_automata(){tot=0;}
//Insert c
inline void Insert(int n,int id){
int p=0;
for(int i=1;i<=n;i++){
if(ch[p].find(c[i])==ch[p].end()) ch[p][c[i]]=++tot;
p=ch[p][c[i]];
}
end[p].push_back(id);
}
inline void BuildFail(){
queue<int> q;while(q.size()) q.pop();
for(it i=ch[0].begin();i!=ch[0].end();i++) q.push(i->second);
while(q.size()){
int top=q.front();q.pop();
for(it i=ch[top].begin();i!=ch[top].end();i++){
int p=top,now=i->second;
node c=i->first;
p=fail[p];
while(p&&ch[p].find(c)==ch[p].end()) p=fail[p];
if(ch[p].find(c)!=ch[p].end()) p=ch[p][c];
fail[now]=p;q.push(now);
last[now]=end[p].empty()?last[p]:p;
}
}
}
inline void GetAns(int n){
int now=0;
for(int i=1;i<=n;i++){
// printf("now:%d\n",now);
int p=now;
while(p&&ch[p].find(c[i])==ch[p].end()) p=fail[p];
// printf("p0:%d\n",p);
// c[i].Print();puts("");
if(ch[p].find(c[i])!=ch[p].end()) p=ch[p][c[i]];
// printf("now1:%d\n",now);printf("p1:%d\n",p);
now=p;
for(;p;p=last[p]) cnt[p]++;
// printf("p:%d\n",p);
}
}
inline void FinaGetAns(){
for(int i=1;i<=tot;i++){
for(int j=0;j<end[i].size();j++){
ans[end[i][j]]+=cnt[i];
if(vis[end[i][j]]) ans[end[i][j]]/=2;
}
}
}
inline void Print(){
printf("tot:%d\n",tot);
for(int i=1;i<=tot;i++){
printf("i:%d fail:%d\n",i,fail[i]);
}
for(int i=0;i<=tot;i++){
printf("i:%d cnt:%d\n",i,cnt[i]);
}
}
};
AC_automata ac;
inline void Print(){
for(int i=1;i<=tail;i++) c[i].Print();puts("");
}
//change a[1,k] into an order c
inline void GetNewOrder(int k,int id){
tail=0;vis[id]=1;
for(int i=1;i<=k-2;i++){
int len1=Dis(a[i],a[i+1]);
int len2=Dis(a[i+1],a[i+2]);
int g=gcd(len1,len2);len1/=g;len2/=g;
int DotProduct=(Vector(a[i+2]-a[i+1])*Vector(a[i]-a[i+1]));
int CrossProduct(Vector(a[i+2]-a[i+1])^Vector(a[i]-a[i+1]));
g=gcd(abs(DotProduct),abs(CrossProduct));
DotProduct/=g;CrossProduct/=g;
c[++tail]=node(len1,len2,DotProduct,CrossProduct);
if(CrossProduct) vis[id]=0;
}
}
inline void intt(){
read(n);read(m);
for(int i=1;i<=m;i++){
int k;read(k);
for(int j=1;j<=k;j++) a[j].Intt();
if(k<=2){
if(k==1) ans[i]=n;else ans[i]=n-1;
continue;
}
GetNewOrder(k,i);ac.Insert(tail,i);
}
ac.BuildFail();
// ac.Print();
}
int main(){
// freopen("my.in","r",stdin);
// freopen("my.out","w",stdout);
intt();
// Print();
for(int i=1;i<=n;i++) a[i].Intt();
GetNewOrder(n,0);ac.GetAns(tail);
// Print();ac.Print();
for(int i=1;i<=n;i++) a[i].y*=-1;
GetNewOrder(n,0);ac.GetAns(tail);
// Print();ac.Print();
ac.FinaGetAns();
for(int i=1;i<=m;i++) printf("%d\n",ans[i]);
return 0;
}
1.6 Simpsons’ Hidden Talents
EXKMP 裸题。我们直接跑就可以。
#include<bits/stdc++.h>
#define dd double
#define ld long double
#define ll long long
#define uint unsigned int
#define ull unsigned long long
#define N 50010
#define M number
using namespace std;
const int INF=0x3f3f3f3f;
template<typename T> inline void read(T &x) {
x=0; int f=1;
char c=getchar();
for(;!isdigit(c);c=getchar()) if(c == '-') f=-f;
for(;isdigit(c);c=getchar()) x=x*10+c-'0';
x*=f;
}
template<typename T> T Max(T a,T b){
return a<b?b:a;
}
template<typename T> T Min(T a,T b){
return a<b?a:b;
}
int nxt[N],extend[N],len1,len2;
char s1[N],s2[N];
inline void GetNext(){
nxt[0]=len1;int now=0;
while(s1[now]==s1[1+now]&&now+1<len1) now++;
nxt[1]=now;
int p0=1;
for(int i=2;i<len1;i++){
if(i+nxt[i-p0]<nxt[p0]+p0) nxt[i]=nxt[i-p0];
else{
int now=nxt[p0]+p0-i;
now=Max(now,0);
while(s1[now]==s1[i+now]&&now+i<len1) now++;
nxt[i]=now;p0=i;
}
}
}
inline void GetZ(){
GetNext();int now=0;
while(s1[now]==s2[now]&&now<Min(len2,len1)) now++;
extend[0]=now;
int p0=0;
for(int i=1;i<len2;i++){
if(i+nxt[i-p0]<extend[p0]+p0) extend[i]=nxt[i-p0];
else{
int now=extend[p0]+p0-i;
now=Max(now,0);
while(s1[now]==s2[i+now]&&now<len1&&now+i<len2) now++;
extend[i]=now;p0=i;
}
}
}
int main(){
// freopen("my.in","r",stdin);
// freopen("my.out","w",stdout);
ios::sync_with_stdio(false);cin.tie(0);
while(cin>>s1>>s2){
len1=strlen(s1);len2=strlen(s2);
GetZ();
// for(int i=0;i<len2;i++) printf("%d\n",extend[i]);
bool op=0;int ans;
for(int i=0;i<len2;i++){
if(extend[i]==len2-i){
printf("%s %d\n",s2+i,len2-i);
op=1;break;
}
}
if(!op) printf("0\n");
memset(nxt,0,sizeof(nxt));
memset(extend,0,sizeof(extend));
}
}
1.7 Problem M. Mediocre String Problem
我们考虑这个串首先是基于 \(s\) 串中的一个回文串,然后我们把 \(s\) 翻转,跑一遍 EXKMP,拿到 extend 数组。然后我们枚举回文中心,统计 extend 的前缀和就可以。
代码:
#include<bits/stdc++.h>
#define dd double
#define ld long double
#define ll long long
#define uint unsigned int
#define ull unsigned long long
#define N 1000050
#define M number
using namespace std;
const int INF=0x3f3f3f3f;
template<typename T> inline void read(T &x) {
x=0; int f=1;
char c=getchar();
for(;!isdigit(c);c=getchar()) if(c == '-') f=-f;
for(;isdigit(c);c=getchar()) x=x*10+c-'0';
x*=f;
}
template<typename T> inline T Min(T a,T b){
return a<b?a:b;
}
template<typename T> inline T Max(T a,T b){return a<b?b:a;}
char s[N],t[N];
int lens,lent,nxt[N],extend[N];
ll ans,sum[N];
inline void GetNext(){
nxt[0]=lent;int len=0;int p0=1;
while(len+1<lent&&t[len+1]==t[len]) len++;nxt[1]=len;
for(int i=2;i<=lent-1;i++){
if(i+nxt[i-p0]<p0+nxt[p0]) nxt[i]=nxt[i-p0];
else{
int now=p0+nxt[p0]-i;now=Max(now,0);
while(now+i<lent&&t[now]==t[i+now]) now++;
nxt[i]=now;p0=i;
}
}
}
inline void ExKmp(){
int len=0;int p0=0;
while(len<lens&&len<lent&&t[len]==s[len]) len++;extend[0]=len;
for(int i=1;i<lens;i++){
if(i+nxt[i-p0]<p0+extend[p0]) extend[i]=nxt[i-p0];
else{
int now=p0+extend[p0]-i;now=Max(now,0);
while(now<lent&&now+i<lens&&t[now]==s[i+now]) now++;
extend[i]=now;p0=i;
}
}
}
char Ma[N<<1];
int len[N<<1],Len;
inline void PreManacher(){
for(int i=0;i<=(lens-1);i++){
Ma[i<<1]='#';
Ma[i<<1|1]=s[i];
}
Ma[lens<<1]='#';Len=lens<<1;
// puts(Ma);
}
inline void Manacher(){
PreManacher();
int mid=0,maxright=0;
for(int i=0;i<=Len;i++){
if(i<maxright) len[i]=Min(len[(mid<<1)-i],len[mid]+mid-i);
while(i-len[i]>=0&&Ma[i-len[i]]==Ma[i+len[i]]) len[i]++;
if(maxright<len[i]+i){maxright=len[i]+i;mid=i;}
}
}
inline int Ref(int x){
if((x&1)==0) x--;return (x-1)/2;
}
inline ll GetSum(int l,int r){
if(r<l) return 0;if(l<=0) return sum[r];return sum[r]-sum[l-1];
}
int main(){
// freopen("my.in","r",stdin);
// freopen("my.out","w",stdout);
scanf("%s%s",s,t);lens=strlen(s);lent=strlen(t);
reverse(s,s+lens);GetNext();ExKmp();Manacher();
sum[0]=extend[0];for(int i=1;i<lens;i++) sum[i]=sum[i-1]+extend[i];
// for(int i=0;i<lens;i++) printf("%d ",sum[i]);puts("");
// for(int i=0;i<lens;i++) printf("%d ",extend[i]);puts("");
// for(int i=0;i<=Len;i++) printf("%d ",len[i]);puts("");
for(int i=0;i<=Len;i++){
int l=-1,r=-1;
if(Ma[i]=='#'){
l=i+3,r=i+len[i];
// if(i<=2) continue;
r=Min(r,i<<1|1);r=Min(r,Len-1);
l=Ref(l);r=Ref(r);
ans=ans+GetSum(l,r);
}
else{
l=i+2,r=i+len[i];
// if(i<=1) continue;
r=Min(r,i<<1|1);r=Min(r,Len-1);
l=Ref(l);r=Ref(r);
ans=ans+GetSum(l,r);
}
// printf("l:%d r:%d\n",l,r);printf("%d\n",GetSum(l,r));
// printf("i:%d ans:%d\n",i,ans);
}
printf("%lld\n",ans);
return 0;
}