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

 Problem Address:http://poj.org/problem?id=1625

 

【前言】

这是我在POJ上的第100道题。

前天做到了99,本来想随便找一道完成100的,结果没找着。

昨天找了一道自动机+DP的。

提交后返回WA。

得知是高精度,才改写了加法运算。

返回了AC,成功达到100。

昨天CSDN说是什么七一维护,没法发解题报告。

特此纪念第一百道题。

 

【思路】

同上一篇一样的思路,构造出AC自动机,包括空节点和失败指针的构造,以及所谓子串为非法串的串也为非法串。

这样之后就可以开始DP。

思路也一样。

不同的是,转移方程为dp[i][j] = dp[i][j] + dp[i-1][k],同时满足k和j所在节点均不能为某个非法串结尾。

在这个基础上,完成高精度,大功告成!

 

【代码】

#include <iostream>
using namespace std;

struct node
{
	int index;
	node *fail;
	node *next[50];
	int count;
}trie[1000];

int total;
node *root;

node *q[1000];
int head, tail;

char word[100];

struct num
{
	int number[101];
	int len;
};

num dp[100][100];

void add(num a, num &t)
{
	int i;
	for (i=0; i<a.len && i<t.len; i++)
		t.number[i] += a.number[i];
	if (a.len>t.len)
	{
		for (i=t.len; i<a.len; i++)
		{
			t.number[i] = a.number[i];
		}
		t.len = a.len;
	}
	for (i=1; i<t.len; i++)
	{
		t.number[i] += t.number[i-1]/10;
		t.number[i-1] %= 10;
	}
	while (t.number[t.len-1]>10)
	{
		t.number[t.len] = t.number[t.len-1]/10;
		t.number[t.len-1] %= 10;
		t.len++;
	}
}

int rank[300];
int ct;

int n,m,p;

inline node *new_node()
{
	node *p = &trie[total];
	p->index = total;
	p->count = 0;
	p->fail = NULL;
	int i;
	for (i=0; i<n; i++)
		p->next[i] = NULL;
	total++;
	return p;
}

void insert(char *s)
{
	node *p = root;
	int index;
	int i = 0;
	while(s[i]!='\0')
	{
		index = rank[s[i]];
		if (p->next[index]==NULL)
			p->next[index] = new_node();
		p = p->next[index];
		i++;
	}
	p->count++;
}

void build_ac_automation()
{
	int i;
	node *temp;
	head = 1;
	tail = 0;
	q[0] = root;
	while(head!=tail)
	{
		temp = q[tail];
		tail++;
		for (i=0; i<n; i++)
		{
			if (temp->next[i]!=NULL)
			{
				if (temp==root)
					temp->next[i]->fail = root;
				else
				{
					temp->next[i]->fail = temp->fail->next[i];
					if (temp->next[i]->fail->count!=0)
						temp->next[i]->count = 1;
				}
				q[head] = temp->next[i];
				head++;
			}
			else
			{
				if (temp==root)
					temp->next[i] = root;
				else
					temp->next[i] = temp->fail->next[i];
			}

		}
	}
}

num solve()
{
	int i,j,k;
	int index;
	for (i=0; i<=m; i++)
	{
		for (j=0; j<=total; j++)
		{
			dp[i][j].len = 1;
		}
	}
	dp[0][0].len = 1;
	dp[0][0].number[0] = 1;
	for (i=1; i<=m; i++)
	{
		for (j=0; j<total; j++)
		{
			if (trie[j].count==0)
			{
				for (k=0; k<n; k++)
				{
					if (trie[j].next[k]->count==0)
					{
						index = trie[j].next[k]->index;
						add(dp[i-1][j], dp[i][index]);
					}
				}
			}
		}
	}
	num sum;
	sum.len = 1;
	sum.number[0] = 0;
	for (i=0; i<total; i++)
	{
		if (trie[i].count==0)
		{
			add(dp[m][i], sum);
		}
	}
	return sum;
}

int main()
{
	int i;
	scanf("%d %d %d", &n, &m, &p);
	scanf("%s", word);
	for (i=0,ct=0; word[i]!='\0'; i++)
	{
		rank[word[i]] = ct;
		ct++;
	}
	total = 0;
	root = new_node();
	for (i=0; i<p; i++)
	{
		scanf("%s", word);
		insert(word);
	}
	build_ac_automation();
	num t = solve();
	for (i=t.len-1; i>=0; i--)
		printf("%d", t.number[i]);
	printf("\n");
	return 0;
}


【P.S】

话说TC还没上去做过。

以后可能刷刷SGU。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值