zoukankan      html  css  js  c++  java
  • Codeforces 432 D.Prefixes and Suffixes (Z Algorithm) / (KMP+dp)

    题目链接:

    http://codeforces.com/contest/432/problem/D

    题意:

    给你一个字符串,找到所有前缀与后缀相同的位置,以及这个前缀在整个字符串出现的次数

    ABACABA ==>  pre: A  suf:A --->  1 4  在1这个位置有相同的后缀,A出现了4次

           pre: ABA  suf: ABA  ----> 3 2 在3这个位置有相同的后缀,ABA出现了2次

    思路:

    方法一:   Z Algorithm  新技能get   http://courses.csail.mit.edu/6.006/spring11/lectures/lec18-extra.pdf

        Z Algorithm z[i]表示第i个位置与前缀匹配了多少个字符
        比如 A B C D E A B C D E A B C
                0 0  0 0 0  8 0 0  0 0 3  0 0

        我们用非常巧妙的方式得到了z数组 [ 虽然不太懂,感觉和求manacher的回文半径有点相似]

        那么 当z[n-i-1]==i+1 的时候,它就是答案,就是说第n-i+1个位置延伸到最后了,也就是后缀与前缀匹配了,对于其他不等的z,我们对匹配的相同长度的计数

        逆推回去得到出现几次的答案,看代码把。。

    方法二: KMP+dp。对next数组,理解深了一点

        ABABCDABAB

        p   next

        0   0
        1   0
        2   0
        3   1
        4   2
        5   0
        6   0
        7   1
        8   2
        9   3
        10 4

        每次从最后往前跳,比如从第10个位置跳到了第4个位置,那么前四个与后四个是一样的,对于新的串ABAB, 相当于ABAB还是后缀,再继续跳,直到跳到0

        所以跳了几次,就有几个位置与后缀匹配了。

        然后 dp,dp[i]表示从1到 i 这个子串出现过的次数,转化为 算next[i]出现了几次 , 从后往前推 dp[Next[i]] += dp[i]; 初始dp[i]=1

    代码一:

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    #define MS(a) memset(a,0,sizeof(a))
    #define MP make_pair
    #define PB push_back
    const int INF = 0x3f3f3f3f;
    const ll INFLL = 0x3f3f3f3f3f3f3f3fLL;
    inline ll read(){
        ll x=0,f=1;char ch=getchar();
        while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
        while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
        return x*f;
    }
    //////////////////////////////////////////////////////////////////////////
    const int maxn = 1e5+10;
    
    string s;
    // Z Algorithm z[i]表示第i个位置与前缀匹配了多少个字符
    // 比如 ABCDEABCDEABC
    //      0000080000300
    int z[maxn],a[maxn],f[maxn];
    
    int main(){
        cin >> s;
        int n = s.size();
        int l=0,r=0;
        for(int i=1; i<=n-1; i++){
            if(i <= r) z[i] = min(r-i+1,z[i-l]);
            while(i+z[i]<n && s[z[i]]==s[i+z[i]]) z[i]++;
            if(i+z[i]-1 > r){
                l = i;
                r = i+z[i]-1;
            }
        }
        for(int i=0; i<n; i++) a[z[i]]++; // 与前缀匹配的相同长度的有几个
        for(int i=n; i>=0; i--){
            f[i] += f[i+1]; // f数组表示匹配长度为i的有几个[不包括前缀], 要逆推,累加,因为长度长的匹配上了,那也一定符合长度短的。
            f[i] += a[i];
        }
        int cnt = 0;
        for(int i=0; i<n; i++){
            if(z[n-i-1] == i+1)
                cnt++;
        }
        cnt++; // 整个串, z[0]=0!=n, 所以没算进答案中
        cout << cnt << endl;
        for(int i=0; i<n; i++){
            if(z[n-i-1]==i+1){
                cout << i+1 << " " << f[i+1]+1 << endl;
            }
        }
        cout << n << " " << 1 << endl;
    
        return 0;
    }

    代码二:

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    #define MS(a) memset(a,0,sizeof(a))
    #define MP make_pair
    #define PB push_back
    const int INF = 0x3f3f3f3f;
    const ll INFLL = 0x3f3f3f3f3f3f3f3fLL;
    inline ll read(){
        ll x=0,f=1;char ch=getchar();
        while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
        while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
        return x*f;
    }
    //////////////////////////////////////////////////////////////////////////
    const int maxn = 1e5+10;
    
    char s[maxn];
    int Next[maxn],pos[maxn],dp[maxn];
    
    void getNext(){
        int len = strlen(s);
        Next[0] = 0, Next[1] = 0;
        for(int i=1; i<len; i++){
            int j = Next[i];
            while(j && s[i]!=s[j]) j = Next[j];
            Next[i+1] = (s[i]==s[j] ? j+1 : 0);
        }
    }
    
    int main(){
        cin >> s;
        getNext();
        for(int i=0; i<=strlen(s); i++)
            cout << i << " " << Next[i] << endl;
        int n = strlen(s);
        int cnt = 0;
        for(int i=Next[n]; i!=0; i=Next[i]){
            pos[cnt++] = i;
        }
        for(int i=0; i<=n; i++) dp[i] = 1;
        for(int i=n; i>=0; i--){
            dp[Next[i]] += dp[i];
        }
        cout << cnt+1 << endl;
        for(int i=cnt-1; i>=0; i--){
            cout << pos[i] << " " << dp[pos[i]] << endl;
        }
        cout << n << " " << 1 << endl;
    
        return 0;
    }
  • 相关阅读:
    hdu1240 bfs 水题
    hdu 2102 BFS
    gym 101081E Polish Fortress 几何
    Gym 101081K Pope's work dp
    hdu 6188 贪心
    hdu 6186 水
    Codeforces Round #430 (Div. 2) A B 水 C dfs,思维 D trie,二进制
    hdu6152 拉姆齐定理
    hdu6165 缩点,dfs
    hdu6153 扩展KMP
  • 原文地址:https://www.cnblogs.com/yxg123123/p/7161944.html
Copyright © 2011-2022 走看看