zoukankan      html  css  js  c++  java
  • Ural 1158. Censored! 有限状态自动机+DP+大整数

    Ural1158 看上去很困难的一道题。

    原文地址 http://blog.csdn.net/prolightsfxjh/article/details/54729646

    题意:给出n个不同的字符,用这n个字符构成长度为m的字符串,要求每个串的子串都不出现给定的p个串中的任一个,求满足要求的字符串的个数。
     
    AC自动机+dp
    因为构成的最终串是由一个字符一个字符添加到字符串尾部构成的,那么如果一个串的后缀如果恰好是某个给定串的前缀时,这个串就可能最终成为非法串。
    用k个给定串建立AC自动机,然后从根节点开始递推,
    dpij表示递推到第j个字符当前在自动机上的i号节点时的方案数,如果下一个节点是k,且不是危险节点,则把dpij加到dp[k][i+1]里,
    跑一遍,然后答案就是所有非危险节点的方案数的和(其实危险节点上都是0)。因为危险节点是给定串的终点或者其后缀节点是危险节点的点,遍历到危险节点的点上的方案必定是包含了给定串的方案,故不能记录这些。
    此外这里dpij会很多,故要用高精度整数。笔者自己收藏的大整数类的版,可行长太长了,所以MLE了一发,调小了才过。⊙﹏⊙‖∣
    复杂度 O(n^3)
     
    模版串长度很小,所以Trie上节点很少,递推成本也就比较低。
     
    其实这道题就是三个部分, 实现了AC自动机和大整数, 就是一道DP题了,分开来看 理解了KMP算法和Trie树 AC自动机很简单, 大整数朴素的O(n^2)实现也很简单,DP方程也很简单,但是组合起来,就很困难了。
    诶 东北欧赛区的题,总是这么~
    代码如下 洋洋洒洒12k 当然不是我写的, 不过这份代码非常清晰易懂,反正智障的我看的挺明白。
     
    #include <iostream>  
    #include <cstdio>  
    #include <cstring>  
    #include <queue>  
    #include <map>  
    using namespace std;  
    const int CHAR_SIZE = 51;  
    const int MAX_SIZE = 105;  
    map<char, int> mp;  
      
    struct AC_Machine{  
        int ch[MAX_SIZE][CHAR_SIZE], danger[MAX_SIZE], fail[MAX_SIZE];  
        int sz;  
      
        void init(){  
            sz = 1;  
            memset(ch[0], 0, sizeof ch[0]);  
            memset(danger, 0, sizeof danger);  
        }  
      
        void _insert(char *s){  
            int n = strlen(s);  
            int u = 0, c;  
            for(int i = 0; i < n; i++){  
                c = mp[s[i]];  
                if(!ch[u][c]){  
                    memset(ch[sz], 0, sizeof ch[sz]);  
                    danger[sz] = 0;  
                    ch[u][c] = sz++;  
                }  
                u = ch[u][c];  
            }  
            danger[u] = 1;  
        }  
      
        void _build(){  
            queue<int> Q;  
            fail[0] = 0;  
            for(int c = 0, u; c < CHAR_SIZE; c++){  
                u = ch[0][c];  
                if(u){Q.push(u); fail[u] = 0;}  
            }  
            int r;  
            while(!Q.empty()){  
                r = Q.front();  
                Q.pop();  
                danger[r] |= danger[fail[r]];  
                for(int c = 0, u; c < CHAR_SIZE; c++){  
                    u = ch[r][c];  
                    if(!u){ch[r][c] = ch[fail[r]][c]; continue; }  
                    fail[u] = ch[fail[r]][c];  
                    Q.push(u);  
                }  
            }  
        }  
    }ac;  
      
    char s[MAX_SIZE];  
      
    #include <string>  
    #include <iostream>  
    #include <iosfwd>  
    #include <cmath>  
    #include <cstring>  
    #include <stdlib.h>  
    #include <stdio.h>  
    #include <cstring>  
    #define MAX_L 205 //最大长度,可以修改  
    using namespace std;  
      
    class bign  
    {  
    public:  
        int len, s[MAX_L];//数的长度,记录数组  
    //构造函数  
        bign();  
        bign(const char*);  
        bign(int);  
        bool sign;//符号 1正数 0负数  
        string toStr() const;//转化为字符串,主要是便于输出  
        friend istream& operator>>(istream &,bign &);//重载输入流  
        friend ostream& operator<<(ostream &,bign &);//重载输出流  
    //重载复制  
        bign operator=(const char*);  
        bign operator=(int);  
        bign operator=(const string);  
    //重载各种比较  
        bool operator>(const bign &) const;  
        bool operator>=(const bign &) const;  
        bool operator<(const bign &) const;  
        bool operator<=(const bign &) const;  
        bool operator==(const bign &) const;  
        bool operator!=(const bign &) const;  
    //重载四则运算  
        bign operator+(const bign &) const;  
        bign operator++();  
        bign operator++(int);  
        bign operator+=(const bign&);  
        bign operator-(const bign &) const;  
        bign operator--();  
        bign operator--(int);  
        bign operator-=(const bign&);  
        bign operator*(const bign &)const;  
        bign operator*(const int num)const;  
        bign operator*=(const bign&);  
        bign operator/(const bign&)const;  
        bign operator/=(const bign&);  
    //四则运算的衍生运算  
        bign operator%(const bign&)const;//取模(余数)  
        bign factorial()const;//阶乘  
        bign Sqrt()const;//整数开根(向下取整)  
        bign pow(const bign&)const;//次方  
    //一些乱乱的函数  
        void clean();  
        ~bign();  
    };  
    #define max(a,b) a>b ? a : b  
    #define min(a,b) a<b ? a : b  
      
    bign::bign()  
    {  
        memset(s, 0, sizeof(s));  
        len = 1;  
        sign = 1;  
    }  
      
    bign::bign(const char *num)  
    {  
        *this = num;  
    }  
      
    bign::bign(int num)  
    {  
        *this = num;  
    }  
      
    string bign::toStr() const  
    {  
        string res;  
        res = "";  
        for (int i = 0; i < len; i++)  
            res = (char)(s[i] + '0') + res;  
        if (res == "")  
            res = "0";  
        if (!sign&&res != "0")  
            res = "-" + res;  
        return res;  
    }  
      
    istream &operator>>(istream &in, bign &num)  
    {  
        string str;  
        in>>str;  
        num=str;  
        return in;  
    }  
      
    ostream &operator<<(ostream &out, bign &num)  
    {  
        out<<num.toStr();  
        return out;  
    }  
      
    bign bign::operator=(const char *num)  
    {  
        memset(s, 0, sizeof(s));  
        char a[MAX_L] = "";  
        if (num[0] != '-')  
            strcpy(a, num);  
        else  
            for (int i = 1; i < strlen(num); i++)  
                a[i - 1] = num[i];  
        sign = !(num[0] == '-');  
        len = strlen(a);  
        for (int i = 0; i < strlen(a); i++)  
            s[i] = a[len - i - 1] - 48;  
        return *this;  
    }  
      
    bign bign::operator=(int num)  
    {  
        char temp[MAX_L];  
        sprintf(temp, "%d", num);  
        *this = temp;  
        return *this;  
    }  
      
    bign bign::operator=(const string num)  
    {  
        const char *tmp;  
        tmp = num.c_str();  
        *this = tmp;  
        return *this;  
    }  
      
    bool bign::operator<(const bign &num) const  
    {  
        if (sign^num.sign)  
            return num.sign;  
        if (len != num.len)  
            return len < num.len;  
        for (int i = len - 1; i >= 0; i--)  
            if (s[i] != num.s[i])  
                return sign ? (s[i] < num.s[i]) : (!(s[i] < num.s[i]));  
        return !sign;  
    }  
      
    bool bign::operator>(const bign&num)const  
    {  
        return num < *this;  
    }  
      
    bool bign::operator<=(const bign&num)const  
    {  
        return !(*this>num);  
    }  
      
    bool bign::operator>=(const bign&num)const  
    {  
        return !(*this<num);  
    }  
      
    bool bign::operator!=(const bign&num)const  
    {  
        return *this > num || *this < num;  
    }  
      
    bool bign::operator==(const bign&num)const  
    {  
        return !(num != *this);  
    }  
      
    bign bign::operator+(const bign &num) const  
    {  
        if (sign^num.sign)  
        {  
            bign tmp = sign ? num : *this;  
            tmp.sign = 1;  
            return sign ? *this - tmp : num - tmp;  
        }  
        bign result;  
        result.len = 0;  
        int temp = 0;  
        for (int i = 0; temp || i < (max(len, num.len)); i++)  
        {  
            int t = s[i] + num.s[i] + temp;  
            result.s[result.len++] = t % 10;  
            temp = t / 10;  
        }  
        result.sign = sign;  
        return result;  
    }  
      
    bign bign::operator++()  
    {  
        *this = *this + 1;  
        return *this;  
    }  
      
    bign bign::operator++(int)  
    {  
        bign old = *this;  
        ++(*this);  
        return old;  
    }  
      
    bign bign::operator+=(const bign &num)  
    {  
        *this = *this + num;  
        return *this;  
    }  
      
    bign bign::operator-(const bign &num) const  
    {  
        bign b=num,a=*this;  
        if (!num.sign && !sign)  
        {  
            b.sign=1;  
            a.sign=1;  
            return b-a;  
        }  
        if (!b.sign)  
        {  
            b.sign=1;  
            return a+b;  
        }  
        if (!a.sign)  
        {  
            a.sign=1;  
            b=bign(0)-(a+b);  
            return b;  
        }  
        if (a<b)  
        {  
            bign c=(b-a);  
            c.sign=false;  
            return c;  
        }  
        bign result;  
        result.len = 0;  
        for (int i = 0, g = 0; i < a.len; i++)  
        {  
            int x = a.s[i] - g;  
            if (i < b.len) x -= b.s[i];  
            if (x >= 0) g = 0;  
            else  
            {  
                g = 1;  
                x += 10;  
            }  
            result.s[result.len++] = x;  
        }  
        result.clean();  
        return result;  
    }  
      
    bign bign::operator * (const bign &num)const  
    {  
        bign result;  
        result.len = len + num.len;  
      
        for (int i = 0; i < len; i++)  
            for (int j = 0; j < num.len; j++)  
                result.s[i + j] += s[i] * num.s[j];  
      
        for (int i = 0; i < result.len; i++)  
        {  
            result.s[i + 1] += result.s[i] / 10;  
            result.s[i] %= 10;  
        }  
        result.clean();  
        result.sign = !(sign^num.sign);  
        return result;  
    }  
      
    bign bign::operator*(const int num)const  
    {  
        bign x = num;  
        bign z = *this;  
        return x*z;  
    }  
    bign bign::operator*=(const bign&num)  
    {  
        *this = *this * num;  
        return *this;  
    }  
      
    bign bign::operator /(const bign&num)const  
    {  
        bign ans;  
        ans.len = len - num.len + 1;  
        if (ans.len < 0)  
        {  
            ans.len = 1;  
            return ans;  
        }  
      
        bign divisor = *this, divid = num;  
        divisor.sign = divid.sign = 1;  
        int k = ans.len - 1;  
        int j = len - 1;  
        while (k >= 0)  
        {  
            while (divisor.s[j] == 0) j--;  
            if (k > j) k = j;  
            char z[MAX_L];  
            memset(z, 0, sizeof(z));  
            for (int i = j; i >= k; i--)  
                z[j - i] = divisor.s[i] + '0';  
            bign dividend = z;  
            if (dividend < divid) { k--; continue; }  
            int key = 0;  
            while (divid*key <= dividend) key++;  
            key--;  
            ans.s[k] = key;  
            bign temp = divid*key;  
            for (int i = 0; i < k; i++)  
                temp = temp * 10;  
            divisor = divisor - temp;  
            k--;  
        }  
        ans.clean();  
        ans.sign = !(sign^num.sign);  
        return ans;  
    }  
      
    bign bign::operator/=(const bign&num)  
    {  
        *this = *this / num;  
        return *this;  
    }  
      
    bign bign::operator%(const bign& num)const  
    {  
        bign a = *this, b = num;  
        a.sign = b.sign = 1;  
        bign result, temp = a / b*b;  
        result = a - temp;  
        result.sign = sign;  
        return result;  
    }  
      
    bign bign::pow(const bign& num)const  
    {  
        bign result = 1;  
        for (bign i = 0; i < num; i++)  
            result = result*(*this);  
        return result;  
    }  
      
    bign bign::factorial()const  
    {  
        bign result = 1;  
        for (bign i = 1; i <= *this; i++)  
            result *= i;  
        return result;  
    }  
      
    void bign::clean()  
    {  
        if (len == 0) len++;  
        while (len > 1 && s[len - 1] == '')  
            len--;  
    }  
      
    bign bign::Sqrt()const  
    {  
        if(*this<0)return -1;  
        if(*this<=1)return *this;  
        bign l=0,r=*this,mid;  
        while(r-l>1)  
        {  
            mid=(l+r)/2;  
            if(mid*mid>*this)  
                r=mid;  
            else  
                l=mid;  
        }  
        return l;  
    }  
      
    bign::~bign()  
    {  
    }  
      
    bign dp[MAX_SIZE][CHAR_MAX];  
      
    int main()  
    {  freopen("t.txt", "r", stdin);  
        #ifdef LOCAL  
        
        //freopen("1.out", "w", stdout);  
        int T = 1;  
        while(T--){  
        #endif // LOCAL  
        //ios::sync_with_stdio(false); cin.tie(0);  
      
        int n, m, p;  
        scanf("%d%d%d", &n, &m, &p);  
        scanf("%s", s);  
        for(int i = 0; i < n; i++){mp[s[i]] = i;}  
        ac.init();  
        while(p--){  
            scanf("%s", s);  
            ac._insert(s);  
        }  
        int i, j, k;  
        for(i = 0; i < ac.sz; i++){  
            for(j = 0; j <= m; j++){  
                dp[i][j] = 0;  
            }  
        }  
        ac._build();  
        dp[0][0] = 1;  
        for(i = 1; i <= m; i++){  
            for(j = 0; j < ac.sz; j++){  
                for(k = 0; k < n; k++){  
                    if(!ac.danger[ac.ch[j][k]]){  
                        dp[ac.ch[j][k]][i] += dp[j][i-1];  
                    }  
                }  
            }  
        }  
        bign ans = 0;  
        for(i = 0; i < ac.sz; i++){ans += dp[i][m];}  
        cout << ans << endl;  
      
        #ifdef LOCAL  
        cout << endl;  
        }  
        #endif // LOCAL  
        return 0;  
    }  
    

      

  • 相关阅读:
    装饰 Markdown
    小技巧
    LINUX 下挂载 exfat 格式 u 盘或移动硬盘
    Matlab 也很强大!
    imageio 载入 Buffer 格式的图片
    Docker 入门
    COCO 数据集使用说明书
    Python 基础 json 与pickle
    Python 基础 常用模块
    Python 基础 装饰器
  • 原文地址:https://www.cnblogs.com/heisenberg-/p/6618368.html
Copyright © 2011-2022 走看看