POJ1625 Censored! AC自动机+dp+高精度

/*
    题目描述:给出一个有n个字符的字符集,再给出p(0 <= p <= 10)个模式串,问长度为m的字符当中有多少个不含有
            任一模式串作为子串。
            
    思路:AC自动机的套路,字典树上的每一个节点表示一种状态,设dp[j][i]表示j节点状态,长度为i的串中满足条件的
        有多少个,则有dp[j][i] = Σdp[k][i - 1],其中k节点可以通过一条树边过渡到j节点。本题结果会比较大,要用高精度。
        
    收获:通过此题和POJ2778,巩固了这样一个事实:AC自动机的字典树中的每一个节点代表了一个状态,并且通过
        树边就可以知道这个状态可以转移到其它的什么状态
*/
#pragma warning(disable:4786)
#pragma comment(linker, "/STACK:102400000,102400000")
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<stack>
#include<queue>
#include<map>
#include<set>
#include<vector>
#include<cmath>
#include<string>
#include<sstream>
#include<bitset>
#define LL long long
#define FOR(i,f_start,f_end) for(int i=f_start;i<=f_end;++i)
#define mem(a,x) memset(a,x,sizeof(a))
#define lson l,m,x<<1
#define rson m+1,r,x<<1|1
using namespace std;
const int INF = 0x3f3f3f3f;
const double PI = acos(-1.0);
const double eps = 1e-6;
const int MAXNODE = 50 * 50 + 5;
const int MAXS = 12;
const int NUM = 105;
const int MAX_SIGMA_SIZE = 55;
int SIGMA_SIZE;
int maxn;
int vis[500];
/*
* 完全大数模板
* 输出cin>>a
* 输出a.print();
* 注意这个输入不能自动去掉前导0的,可以先读入到char数组,去掉前导0,再用构造函数。
*/
#define MAXN 9999
#define MAXSIZE 50
#define DLEN 4

class BigNum
{
public:
	int a[MAXSIZE];  //可以控制大数的位数
	int len;
public:
	BigNum(){ len = 1; memset(a, 0, sizeof(a)); }  //构造函数
	BigNum(const int);     //将一个int类型的变量转化成大数
	BigNum(const char*);   //将一个字符串类型的变量转化为大数
	BigNum(const BigNum &); //拷贝构造函数
	BigNum &operator=(const BigNum &); //重载赋值运算符,大数之间进行赋值运算
	friend istream& operator>>(istream&, BigNum&); //重载输入运算符
	friend ostream& operator<<(ostream&, BigNum&); //重载输出运算符

	BigNum operator+(const BigNum &)const;  //重载加法运算符,两个大数之间的相加运算
	BigNum operator-(const BigNum &)const;  //重载减法运算符,两个大数之间的相减运算
	BigNum operator*(const BigNum &)const;  //重载乘法运算符,两个大数之间的相乘运算
	BigNum operator/(const int &)const;     //重载除法运算符,大数对一个整数进行相除运算

	BigNum operator^(const int &)const;     //大数的n次方运算
	int operator%(const int &)const;        //大数对一个int类型的变量进行取模运算
	bool operator>(const BigNum &T)const;   //大数和另一个大数的大小比较
	bool operator>(const int &t)const;      //大数和一个int类型的变量的大小比较

	void print();        //输出大数
};
BigNum::BigNum(const int b)   //将一个int类型的变量转化为大数
{
	int c, d = b;
	len = 0;
	memset(a, 0, sizeof(a));
	while (d>MAXN)
	{
		c = d - (d / (MAXN + 1))*(MAXN + 1);
		d = d / (MAXN + 1);
		a[len++] = c;
	}
	a[len++] = d;
}
BigNum::BigNum(const char *s)  //将一个字符串类型的变量转化为大数
{
	int t, k, index, L, i;
	memset(a, 0, sizeof(a));
	L = strlen(s);
	len = L / DLEN;
	if (L%DLEN)len++;
	index = 0;
	for (i = L - 1; i >= 0; i -= DLEN)
	{
		t = 0;
		k = i - DLEN + 1;
		if (k<0)k = 0;
		for (int j = k; j <= i; j++)
			t = t * 10 + s[j] - '0';
		a[index++] = t;
	}
}
BigNum::BigNum(const BigNum &T) :len(T.len)  //拷贝构造函数
{
	int i;
	memset(a, 0, sizeof(a));
	for (i = 0; i<len; i++)
		a[i] = T.a[i];
}
BigNum & BigNum::operator=(const BigNum &n)  //重载赋值运算符,大数之间赋值运算
{
	int i;
	len = n.len;
	memset(a, 0, sizeof(a));
	for (i = 0; i<len; i++)
		a[i] = n.a[i];
	return *this;
}
istream& operator>>(istream &in, BigNum &b)
{
	char ch[MAXSIZE * 4];
	int i = -1;
	in >> ch;
	int L = strlen(ch);
	int count = 0, sum = 0;
	for (i = L - 1; i >= 0;)
	{
		sum = 0;
		int t = 1;
		for (int j = 0; j<4 && i >= 0; j++, i--, t *= 10)
		{
			sum += (ch[i] - '0')*t;
		}
		b.a[count] = sum;
		count++;
	}
	b.len = count++;
	return in;
}
ostream& operator<<(ostream& out, BigNum& b)  //重载输出运算符
{
	int i;
	cout << b.a[b.len - 1];
	for (i = b.len - 2; i >= 0; i--)
	{
		printf("%04d", b.a[i]);
	}
	return out;
}
BigNum BigNum::operator+(const BigNum &T)const   //两个大数之间的相加运算
{
	BigNum t(*this);
	int i, big;
	big = T.len>len ? T.len : len;
	for (i = 0; i<big; i++)
	{
		t.a[i] += T.a[i];
		if (t.a[i]>MAXN)
		{
			t.a[i + 1]++;
			t.a[i] -= MAXN + 1;
		}
	}
	if (t.a[big] != 0)
		t.len = big + 1;
	else t.len = big;
	return t;
}
BigNum BigNum::operator*(const BigNum &T)const  //两个大数之间的相乘
{
	BigNum ret;
	int i, j, up;
	int temp, temp1;
	for (i = 0; i<len; i++)
	{
		up = 0;
		for (j = 0; j<T.len; j++)
		{
			temp = a[i] * T.a[j] + ret.a[i + j] + up;
			if (temp>MAXN)
			{
				temp1 = temp - temp / (MAXN + 1)*(MAXN + 1);
				up = temp / (MAXN + 1);
				ret.a[i + j] = temp1;
			}
			else
			{
				up = 0;
				ret.a[i + j] = temp;
			}
		}
		if (up != 0)
			ret.a[i + j] = up;
	}
	ret.len = i + j;
	while (ret.a[ret.len - 1] == 0 && ret.len>1)ret.len--;
	return ret;
}

void BigNum::print()   //输出大数
{
	int i;
	printf("%d", a[len - 1]);
	for (i = len - 2; i >= 0; i--)
		printf("%04d", a[i]);
	printf("\n");
}
bool ONE(BigNum a)
{
	if (a.len == 1 && a.a[0] == 1)return true;
	else return false;
}
BigNum A, B, X, Y;
struct AhoCorasickAutomata
{
	int ch[MAXNODE][MAX_SIGMA_SIZE];
	int f[MAXNODE];
	int last[MAXNODE];
	int val[MAXNODE];
	int ok[MAXNODE];
	int sz;
	void init()
	{
		mem(ch[0], 0);
		sz = 1;
	}
	int idx(char c)
	{
		return vis[c];
	}

	void insert(char * s)
	{
		int u = 0, n = strlen(s);
		for (int i = 0; i<n; i++){
			int c = idx(s[i]);
			if (!ch[u][c]){
				mem(ch[sz], 0);
				val[sz] = 0;
				ch[u][c] = sz++;
			}
			u = ch[u][c];
		}
		++val[u];
	}

	void print(int j)
	{
		if (j){
			printf("%d: %d\n", j, val[j]);
			print(last[j]);
		}
	}

	void getFail()
	{
		queue<int>Q;
		f[0] = 0;
		for (int i = 0; i < SIGMA_SIZE; i++){
			int u = ch[0][i];
			if (u){
				f[u] = 0;
				Q.push(u);
				last[u] = 0;
			}
		}
		while (!Q.empty()){
			int r = Q.front();          Q.pop();
			for (int c = 0; c< SIGMA_SIZE; c++){
				int u = ch[r][c];
				if (!u){
					ch[r][c] = ch[f[r]][c];         continue;
				}
				Q.push(u);
				int v = f[r];
				f[u] = ch[v][c];
				last[u] = val[f[u]] ? f[u] : last[f[u]];
			}
		}
	}

}ac;
char p[55];
BigNum dp[502][52];
int main()
{
	int m, fw;
	while (scanf("%d %d %d", &SIGMA_SIZE, &m, &fw) != EOF){
		ac.init();
		mem(vis, 0);
		getchar();
		gets(p);
		for (int i = 0; i < SIGMA_SIZE; i++){
			vis[p[i]] = i;
		}
		for (int i = 1; i <= fw; i++){
			gets(p);
			ac.insert(p);
		}
		ac.getFail();
		for (int i = 0; i <= m; i++){
			for (int j = 0; j < ac.sz; j++){
				dp[j][i] = BigNum(0);
			}
		}
		dp[0][0] = BigNum(1);
		for (int i = 0; i < m; i++){
			for (int j = 0; j < ac.sz; j++){
				if (ac.val[j] || ac.last[j])	continue;
				for (int c = 0; c < SIGMA_SIZE; c++){
					int u = ac.ch[j][c];
					if (ac.val[u] || ac.last[u])	continue;
					dp[u][i + 1] = dp[u][i + 1] + dp[j][i];
				}
			}
		}
		BigNum ans = BigNum(0);
		for (int i = 0; i < ac.sz; i++){
			if (!ac.val[i] && !ac.last[i])
			ans = ans + dp[i][m];
		}
		cout << ans << endl;
	}
	return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值