1. 程式人生 > >On the way

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陣列。最後要形成的字串一定是原字串的字尾是原字串的一個字首,最後字首-字尾加到後面。因此要找到最小的字串。

lennext[len] 來求出最小迴圈節。

如果是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;
}

求最長的相等的 s1 字首和 s2 字尾。

s1 求next,然後 s2 為主串 s1 為模式串做kmp就行了。

#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差不多。。。

dp[i] 表示 s[1...i] 這個串的字尾等於 s 串字首的數量。

dp[i]=dp[next[i]]+1

#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;
}

題意是求出做任意次將最前面的字母放到最後面的操作後,字典序最小的串。

演算法就是令 i=0,j=1 。表示以 i 開頭的串和以 j 開頭的串。每次找出第一個 k 使得 s[i+k]!=s[j+k] 。如果 s[i+k]>s[j+k] ,那麼 i 串不可能是最小表示,令 i=i+k+1 ,繼續。同理,若 s[i+k]>s[j+k] ,令 j=j+k+1 ,繼續。若 i=j ,要使得 j+=1 。最後 ij 較小的那個就是答案。

這樣相當於 O(n) 地比較了 n 個不同的串。

#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;
}

題意是求將一個串分成兩段,求價值之和的最大值。

一個串的價值定義為:如果它是迴文串,價值為所有位權值之和,否則價值為 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;
}