HDU 2243 考研路茫茫——单词情结 AC自动机 加 矩阵乘法

21 篇文章 0 订阅
7 篇文章 0 订阅
  题目网址:http://acm.hdu.edu.cn/showproblem.php?pid=2243                                                                                                  这题用的是AC自动机加矩阵乘法去做的。首先根据给的词根建立字典树,然后建立AC自动机,利用fail指针去构造矩阵。矩阵的意思是不包含词根的状态,而把矩阵0行0到sizes-1列的值相加,就是单词长度为一时,不包含词根的情况有多少种。用最大数去减,就是单词长度为一时,包含词根的种数。同样的,矩阵相乘后,乘了N次,用最大数去减,便是长度为N的单词长度包含词根的情况,把1到N的种类相加便是答案。注意,在这里的代码中,我直接把矩阵N阶的和表示了出来,所以直接减就是答案。这里求矩阵N阶的和用的是二分法,不然会超时,假设N是一阶矩阵,N^4 = N^1 + N^2 + N^2 * (N^1 + N^2).二分法中为奇数时特殊处理。
#include <stdio.h>
#include <string.h>
#include <iostream>
#include <algorithm>
#include <math.h>
#include <queue>
#define maxc 35
#define maxn 26
#define mem(a) memset(a, 0, sizeof(a))
using namespace std;

int tree[maxc][maxn], fails[maxc], words[6], sizes;
unsigned __int64 maps[maxc][maxc], ress[maxc][maxc], tmps[maxc][maxc], tmpss[maxc][maxc], danwei[maxc][maxc], ans, rs, rss;
bool num[maxc];
char ch[6];

void inits()
{
	sizes = 1;
	num[0] = 0;
	mem(tree[0]);
	mem(maps);
	mem(ress);
	mem(tmps);
	return;
}

void build()
{
	int i, a, b = 0;
	for(i = 1;i <= words[0];i++)
	{
		a = words[i];
		if(!tree[b][a])
		{
			mem(tree[sizes]);
			num[sizes] = 0;
			tree[b][a] = sizes++;
		}
		b = tree[b][a];
	}
	num[b] = 1;
	return;
}

void ac_machine_build()
{
	int i, a, b;
	queue<int> q;
	fails[0] = 0;
	for(i = 0;i < maxn;i++)
	{
		a = tree[0][i];
		if(a)
		{
			fails[a] = 0;
			q.push(a);
		}
	}
	while(!q.empty())
	{
		b = q.front(); 
		q.pop();
		if(num[fails[b]])
		num[b] = 1;
		for(i = 0;i < maxn;i++)
		{
			a = tree[b][i];
			if(!a)
			{
				tree[b][i] = tree[fails[b]][i];
				continue;
			}
			q.push(a);
			fails[a] = tree[fails[b]][i];
		}
	}
	return;
}

void zh(char *word)
{
	int i, len;
	len = strlen(word);
	words[0] = len;
	for(i = 0;i < len;i++)
	words[i + 1] = ch[i] - 'a';
	return;
}

void mps()
{
	int i, j;
	mem(maps);
	for(i = 0;i < sizes;i++)
	{
		for(j = 0;j < maxn;j++)
		{
			if(!num[i]&&!num[tree[i][j]])
			maps[i][tree[i][j]]++;
		}
	}
	return;
}


void mp_add(unsigned __int64 a[][maxc], unsigned __int64 b[][maxc])
{
	int i, j;
	for(i = 0;i < sizes;i++)
	{
		for(j = 0;j < sizes;j++)
		{
			a[i][j] = a[i][j] + b[i][j];
		}
	}
	return;
}

void mp_sub(unsigned __int64 a[][maxc], unsigned __int64 b[][maxc])
{
	int i, j;
	for(i = 0;i < sizes;i++)
	{
		for(j = 0;j < sizes;j++)
		{
			a[i][j] = a[i][j] - b[i][j];
		}
	}
	return;
}

void mp_mt(unsigned __int64 a[][maxc], unsigned __int64 b[][maxc], unsigned __int64 c[][maxc])
{
	int i, j, k;
	for(i = 0;i < sizes;i++)
	{
		for(j = 0;j < sizes;j++)
		{
			c[i][j] = 0;
			for(k = 0;k < sizes;k++)
			{
				c[i][j] += (a[i][k] * b[k][j]);
			}
		}
	}
	return;
}

void mp_fz(unsigned __int64 a[][maxc], unsigned __int64 b[][maxc])
{
	int i, j;
	for(i = 0;i < sizes;i++)
	{
		for(j = 0;j < sizes;j++)
		{
			a[i][j] = b[i][j];
		}
	}
	return;
}

void mp_swap(unsigned __int64 a[][maxc], unsigned __int64 b[][maxc])
{
	int i, j;
	unsigned long long c[maxc][maxc];
	for(i = 0;i < sizes;i++)
	{
		for(j = 0;j < sizes;j++)
		{
			c[i][j] = a[i][j];
		}
	}
	for(i = 0;i < sizes;i++)
	{
		for(j = 0;j < sizes;j++)
		{
			a[i][j] = b[i][j];
		}
	}
	for(i = 0;i < sizes;i++)
	{
		for(j = 0;j < sizes;j++)
		{
			b[i][j] = c[i][j];
		}
	}
	return;
}

void res(unsigned __int64 n)
{
	if(n == 1)
	{
		int i, j;
		for(i = 0;i < sizes;i++)
		{
			for(j = 0;j < sizes;j++)
			{
				tmpss[i][j] = ress[i][j] = maps[i][j];
			}
		}
		rs = rss = 26;
		return;
	}
	res(n / 2);
	rs = rs * rss + rs;
	rss = rss * rss;
	unsigned __int64 tp[maxc][maxc];
	mp_mt(ress, tmpss, tp);
	mp_add(ress, tp);
	mp_mt(tmpss, tmpss, tmps);
	if(n % 2)
	{
		rs = (rs + 1) * 26;
		rss *= 26;
		mp_add(ress, danwei);
		mp_mt(ress, maps, tp);
		mp_fz(ress, tp);
	    mp_mt(tmps, maps, tmpss);
	}
	else
	{
	    mp_swap(tmps, tmpss);
	}
}

int main()
{
	int i, n;
    unsigned __int64 m, tmp;
	for(i = 0;i < 35;i++)
	{
         danwei[i][i] = 1;
	}
	while(scanf("%d%I64u", &n, &m) != EOF)
	{
		inits();
		ans = 0;
		for(i = 0;i < n;i++)
		{
			mem(ch);
			scanf("%s", ch);
			zh(ch);
			build();
		}
		ac_machine_build();
		mps();
		res(m);
		for(i = 0;i < sizes;i++)
		ans += ress[0][i];
		printf("%I64u\n", rs - ans);
	}
	return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值