On the way
模板題。。
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+7;
int a[N],b[N],n,m,nxt[N];
void getNext(int s[],int n,int nxt[])
{
int i=0,j=nxt[0]=-1;
while(i<n)
{
while(-1!=j&&s[i]!=s[j]) j=nxt[j];
if(s[++i]==s[++j]) nxt[i]=nxt[j];
else nxt[i]=j;
}
}
int kmp(int s[],int n,int pt[],int m)
{
int i=0,j=0;
while(i<n)
{
while(-1!=j&&s[i]!=pt[j]) j=nxt[j];
++i;++j;
if(j>=m) return i-m+1;
}
return -1;
}
int main ()
{
int T;
scanf("%d",&T);
while(T--)
{
scanf("%d%d",&n,&m);
for (int i=0;i<n;++i) scanf("%d",&a[i]);
for(int j=0;j<m;++j) scanf("%d",&b[j]);
getNext(b,m,nxt);
printf("%d\n",kmp(a,n,b,m));
}
return 0;
}
用kmp處理出next陣列。最後要形成的字串一定是原字串的字尾是原字串的一個字首,最後字首-字尾加到後面。因此要找到最小的字串。
用
如果是abcdefgab這種,迴圈節是 abcdefg 。
如果是ababababa這種,迴圈節就是ab。
最後答案就是往後新增多少字元能變成迴圈節的倍數。
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+7;
char s[N];
int nxt[N];
void getNext(char s[],int n,int nxt[])
{
int i=0,j=nxt[0]=-1;
while(i<n)
{
while(-1!=j&&s[i]!=s[j]) j=nxt[j];
++i;++j;
nxt[i]=j;
}
}
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
scanf("%s",s);
int n=strlen(s);
getNext(s,n,nxt);
int circle = n-nxt[n],ans;
if(n%circle==0)
{
if(n==circle) ans=n;
else ans=0;
}
else ans=(-n)%circle+circle;
printf("%d\n",ans);
}
return 0;
}
跟上一題差不多。。。。
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+7;
char s[N];
int nxt[N];
void getNext(char s[],int n,int nxt[])
{
int i=0,j=nxt[0]=-1;
while(i<n)
{
while(-1!=j&&s[i]!=s[j]) j=nxt[j];
++i;++j;
nxt[i]=j;
}
}
int main ()
{
int n,kase=1;
while(~scanf("%d",&n))
{
if(n==0) break;
scanf("%s",s);
getNext(s,n,nxt);
printf("Test case #%d\n",kase++);
for(int i=2;i<=n;++i)
{
int c=i-nxt[i];
if(i%c==0&&i!=c)
printf("%d %d\n",i,i/c);
}
puts("");
}
return 0;
}
求最長的相等的
對
#include<bits/stdc++.h>
using namespace std;
const int N=5e4+7;
char a[N],b[N];
int nxt[N];
void getNext(char s[],int n,int nxt[])
{
int i=0,j=nxt[0]=-1;
while(i<n)
{
while(-1!=j&&s[i]!=s[j]) j=nxt[j];
if(s[++i]==s[++j]) nxt[i]=nxt[j];
else nxt[i]=j;
}
}
int kmp(char s[],int n,char pt[],int m)
{
int i=0,j=0;
while(i<n)
{
while(-1!=j&&s[i]!=pt[j]) j=nxt[j];
++i;++j;
}
return j;
}
int main()
{
while(~scanf("%s %s",a,b))
{
int n=strlen(a);
int m=strlen(b);
getNext(a,n,nxt);
int ans=kmp(b,m,a,n);
if(ans==0) puts("0");
else printf("%s %d\n",b+m-ans,ans);
}
return 0;
}
跟AC自動機dp差不多。。。
#include<bits/stdc++.h>
using namespace std;
const int N=2e5+7;
const int mod=1e4+7;
char s[N];
int nxt[N],dp[N];
void getNext(char s[],int n,int nxt[])
{
int i=0,j=nxt[0]=-1;
while(i<n)
{
while(-1!=j&&s[i]!=s[j]) j=nxt[j];
++i;++j;
nxt[i]=j;
}
}
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
int n;
scanf("%d",&n);
scanf("%s",s);
getNext(s,n,nxt);
int ans=0;
for(int i=1;i<=n;++i)
{
dp[i]=dp[nxt[i]]+1;
ans=(ans+dp[i])%mod;
}
printf("%d\n",ans);
}
return 0;
}
題意是求出做任意次將最前面的字母放到最後面的操作後,字典序最小的串。
演算法就是令
這樣相當於
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+7;
char s[N];
int nxt[N],n;
void getNext()
{
int i=0,j=nxt[0]=-1;
while(i<n)
{
while(-1!=j&&s[i]!=s[j]) j=nxt[j];
if(s[++i]==s[++j]) nxt[i]=nxt[j];
else nxt[i]=j;
}
}
int getMin()
{
int i=0,j=1,l;
while(i<n&&j<n)
{
for(l=0;l<n;++l)
if(s[(i+l)%n]!=s[(j+l)%n]) break;
if(l==n) break;
if(s[(i+l)%n]>s[(j+l)%n]) i=i+l+1;
else j=j+l+1;
if(i==j) ++j;
}
return min(i,j);
}
int getMax()
{
int i=0,j=1,l;
while(i<n&&j<n)
{
for(l=0;l<n;++l)
if(s[(i+l)%n]!=s[(j+l)%n]) break;
if(l==n) break;
if(s[(i+l)%n]<s[(j+l)%n]) i=i+l+1;
else j=j+l+1;
if(i==j) ++j;
}
return min(i,j);
}
int main ()
{
while(~scanf("%s",s))
{
n=strlen(s);
int cir;
getNext();
if(n%(n-nxt[n])==0) cir=n/(n-nxt[n]);
else cir=1;
printf("%d %d %d %d\n",getMin()+1,cir,getMax()+1,cir);
}
return 0;
}
題意是求將一個串分成兩段,求價值之和的最大值。
一個串的價值定義為:如果它是迴文串,價值為所有位權值之和,否則價值為
做Manacher演算法,求出字首是否迴文串和字尾是否迴文串兩個陣列,掃一遍即可。
#include<bits/stdc++.h>
using namespace std;
const int N = 5e5+7;
char tmp[N*2];
int val[26],ma[N*2];
bool a[N*2],b[N*2];
void manache(string &s,int len,char tmp[],int a[])
{
int l=0;
tmp[l++]='$';
tmp[l++]='#';
for(int i=0;i<len;++i)
{
tmp[l++]=s[i];
tmp[l++]='#';
}
tmp[l]=0;
int mx=0,id=0;
for(int i=0;i<l;++i)
{
a[i]=mx>i?min(a[2*id-i],mx-i):1;
while(tmp[a[i]+i]==tmp[i-a[i]]) ++a[i];
if(i+a[i]>mx)
{
mx=i+a[i];
id=i;
}
}
}
int main ()
{
ios::sync_with_stdio(false);
int T;
cin >> T;
while(T--)
{
for(int i=0;i<26;++i) cin >> val[i];
string s;
cin >> s;
int n=s.length();
memset(a,0,sizeof(a));
memset(b,0,sizeof(b));
manache(s,n,tmp,ma);
for(int i=1;i<2*n+2;++i)
if(i-ma[i]==0) a[ma[i]+i-1]=1;
for(int i=2*n;i>0;--i)
if(i+ma[i]==2*n+2) b[i-ma[i]+1]=1;
int ans=0,pre=0,last=0;
for(int i=0;i<n;++i) last+=val[s[i]-'a'];
for(int i=2;i<n*2;++i)
{
if(i%2==0)
{
int v = val[s[(i>>1)-1]-'a'];
pre += v;
last -= v;
}
int res=0;
if(a[i]) res+=pre;
if(b[i]) res+=last;
ans=max(ans,res);
}
cout << ans << '\n';
}
return 0;
}
兩個串相連是迴文串,當且僅當長度小的那個串翻轉後是長度大的那個串的字首,且減去字首後剩下的部分也是迴文串。因此對與一個串,要查詢是多少串的字首。
將字串從大到小排序,插入Trie中,每次插入後,對該串進行一次查詢。Trie上的每個點應該有權值,表明減去字首後的串是否是迴文串。這個可以通過Manacher演算法先預處理出來。
這裡我寫的複雜了點。。其實不需要對長度排序,也不需要插兩次。只需要記錄每個串的字首是否是迴文串和字尾是否是迴文串就行了。
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long ll;
const int N = 2e6+7;
struct Trie
{
int nxt[N][26],end[N],L,root;
int newnode()
{
for(int i=0;i<26;++i)
nxt[L][i]=-1;
end[L]=0;
return L++;
}
void init()
{
L=0;
root=newnode();
}
void insert1(char buf[],bool ok[],int n)
{
int now=root;
for(int i=0;i<n;++i)
{
if(nxt[now][buf[i]-'a']==-1)
nxt[now][buf[i]-'a']=newnode();
now = nxt[now][buf[i]-'a'];
if(ok[i]) ++end[now];
}
}
void insert2(char buf[],bool ok[],int n)
{
int now=root;
for(int i=n-1;i>=0;--i)
{
if(nxt[now][buf[i]-'a']==-1)
nxt[now][buf[i]-'a']=newnode();
now = nxt[now][buf[i]-'a'];
if(ok[i]) ++end[now];
}
}
int query1(char buf[],int n)
{
int now=root;
for(int i=n-1;i>=0;--i)
{
if(nxt[now][buf[i]-'a']==-1) return 0;
now=nxt[now][buf[i]-'a'];
}
return end[now];
}
int query2(char buf[],int n)
{
int now=root;
for(int i=0;i<n;++i)
{
if(nxt[now][buf[i]-'a']==-1) return 0;
now=nxt[now][buf[i]-'a'];
}
return end[now];
}
}t;
char s[N],tmp[N*2];
int ma[N*2];
bool ok[N];
bool manache(char s[],int len,char tmp[],int a[],bool ok[])
{
int l=0;
tmp[l++]='$';
tmp[l++]='#';
for(int i=0;i<len;++i)
{
tmp[l++]=s[i];
tmp[l++]='#';
}
tmp[l]=0;
int mx=0,id=0;
for(int i=0;i<l;++i)
{
a[i]=mx>i?min(a[2*id-i],mx-i):1;
while(tmp[a[i]+i]==tmp[i-a[i]]) ++a[i];
if(i+a[i]>mx)
{
mx=i+a[i];
id=i;
}
}
}
bool deal1(int ma[],int len,bool ok[])
{
for(int i=0;i<len;++i) ok[i]=0;
bool nice=false;
for(int i=2;i<len*2+2;++i)
{
if(ma[i]+i==len*2+2)
{
int id=i-ma[i]+2;
id=(id>>1)-2;
if(id>=0) ok[id]=true;
if(id==-1) nice=true;
}
}
ok[len-1]=true;
return nice;
}
bool deal2(int ma[],int len,bool ok[])
{
for(int i=0;i<len;++i) ok[i]=0;
bool nice=false;
for(int i=2;i<len*2+2;++i)
{
if(i-ma[i]==0)
{
int id=i+ma[i]-2;
id=(id>>1);
if(id<len) ok[id]=true;
if(id==len) nice=true;
}
}
ok[0]=true;
return nice;
}
int len[N],pos[N],p[N];
bool cmp(int x,int y)
{
return len[x]>len[y];
}
int main()
{
int n;
while(~scanf("%d",&n))
{
int tot=0;
for(int i=0;i<n;++i)
{
scanf("%d%s",&len[i],s+tot);
pos[i]=tot;
tot+=len[i];
p[i]=i;
}
sort(p,p+n,cmp);
t.init();
ll ans=0;
for(int i=0;i<n;++i)
{
int id=p[i];
manache(s+pos[id],len[id],tmp,ma,ok);
if(deal1(ma,len[id],ok)) --ans;
t.insert1(s+pos[id],ok,len[id]);
ans+=t.query1(s+pos[id],len[id]);
}
t.init();
for(int i=0;i<n;++i)
{
int id=p[i];
manache(s+pos[id],len[id],tmp,ma,ok);
deal2(ma,len[id],ok);
t.insert2(s+pos[id],ok,len[id]);
ans+=t.query2(s+pos[id],len[id]);
}
printf("%I64d\n",ans);
}
return 0;
}