[FJWC 20190212] T2 原样输出

11 篇文章 0 订阅
5 篇文章 0 订阅

并没有传送门

题目描述

nealchen 是一只copycat。
它会把输入按行读入,原封不动地复制到输出中去。
但是在一次更新以后,它的程序出了一些问题。
它没法输出换行符了。
并且,读入的时候,总会莫名其妙地随机漏掉开头和结尾的若干个字符,甚至整行都会漏掉。
比如orznight 可能会变成rzniorzh 或者空串。
现在你找到一份输入文件丢给nealchen,你想知道它的输出可能有多少种情况,以及每种情况分别是什么。
由于你找到的输入文件全部来自之前的福建省选,所以所有的输入文件每行只可能包含ACGT 四种字符。

输入输出格式

输入格式

从文件copy.in 中读入数据。

第一行一个正整数 n n n ,表示(题面中)输入文件的行数。

接下来 n n n 行,表示输入文件的内容。保证这n 行中每行的每个字符是ACGT四种字符中的一种。

接下来一个整数 k ( 0 ≤ k ≤ 1 ) k(0 \le k \le 1) k(0k1) ,具体含义详见输出格式。

输出格式

输出到文件copy.out 中。

k = 0 k = 0 k=0 ,你需要输出一行,表示输出的可能情况个数模 1 0 9 + 7 10^9 + 7 109+7 的结果。

k = 1 k = 1 k=1 ,你需要按照字典序从小到大输出所有可能的输出情况,一行一个字符串,最后一行输出输出的可能情况个数模 1 0 9 + 7 10^9 + 7 109+7 的结果。

输入输出样例

输入样例#1:
3
AC
CC
AA
0
输出样例#1:
22
输入样例#2:
3
AC
CC
AA
1
输出样例#2:

A
AA
AAA
AC
ACA
ACAA
ACC
ACCA
ACCAA
ACCC
ACCCA
ACCCAA
C
CA
CAA
CC
CCA
CCAA
CCC
CCCA
CCCAA
22
样例2 解释

注意输出的第一行是一个空行。

解题分析

n = 1 n=1 n=1的时候很好做,直接 S A M SAM SAM大力, 本质不同子串个数就直接 D P DP DP, 输出方案按字典序 D F S DFS DFS

考虑多个串的时候会有什么问题: 可能有多种在不同串中取出同一个串的方案。

那么我们强行令取串的方案变成: 如果想取的下一个字符这个串的下一位还有, 就还取这个串的, 否则找到后面第一个有这个字符的串到那个位置去取。

这样每个串就唯一对应了一种取的方案了。 后缀自动机改起来也很简单: 直接在每个后缀自动机没有某个字符的转移的节点添加指向后面第一次出现该字符的自动机的对应节点的转移, d p dp dp和输出方案的方法都一样。

注意有个空串…

代码如下:

#include <cstdio>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include <cctype>
#include <algorithm>
#define R register
#define IN inline
#define W while
#define ll long long
#define MOD 1000000007
#define MX 3005000
int n, cnt, last, cur, ans;
char *str[MX], buf[MX], Out[MX];
int nex[MX][4], lb[MX], rb[MX];
bool exi[MX][4], vis[MX];
int to[MX][4], par[MX], siz[MX], len[MX], root[MX], l[MX];
IN char Get(R char c)
{
	switch (c)
	{
		case 'A': return '0'; break;
		case 'C': return '1'; break;
		case 'G': return '2'; break;
		case 'T': return '3'; break;
	}
}
IN char Give(R int id)
{
	switch (id)
	{
		case 0: return 'A'; break;
		case 1: return 'C'; break;
		case 2: return 'G'; break;
		case 3: return 'T'; break;
	}
}
namespace SAM
{
	IN void insert(R int num, R int id)
	{
		R int now, tar;
		cur = ++cnt; len[cur] = len[last] + 1;
		for (now = last; (~now) && (!to[now][id]); now = par[now]) to[now][id] = cur;
		if (now < 0) return last = cur, par[cur] = 0, void();
		tar = to[now][id];
		if (len[tar] == len[now] + 1) return last = cur, par[cur] = tar, void();
		int nw = ++cnt; len[nw] = len[now] + 1;
		std::memcpy(to[nw], to[tar], sizeof(to[nw]));
		par[nw] = par[tar], par[tar] = par[cur] = nw;
		for (; (~now) && to[now][id] == tar; now = par[now]) to[now][id] = nw;
		last = cur;
	}
	int DFS(R int now)
	{
		if (vis[now]) return siz[now];
		vis[now] = true; siz[now] = 1;
		for (R int i = 0; i < 4; ++i)
		if (to[now][i]) (siz[now] += DFS(to[now][i])) %= MOD;
		return siz[now];
	}
	void DFS(R int now, R int dgt)
	{
		puts(Out); ++ans;
		for (R int i = 0; i < 4; ++i)
		{
			if (!to[now][i]) continue;
			Out[dgt] = Give(i);
			DFS(to[now][i], dgt + 1);
		}
		Out[dgt - 1] = '\0';
	}
}
int main(void)
{
	int typ;
	freopen("copy.in", "r", stdin);
	freopen("copy.out", "w", stdout);
	scanf("%d", &n);
	for (R int i = 1; i <= n; ++i)
	{
		root[i] = ++cnt;
		par[root[i]] = -1;
		scanf("%s", buf + 1);
		l[i] = std::strlen(buf + 1);
		str[i] = new char[l[i] + 5];
		std::memcpy(str[i], buf, l[i] + 3);
		for (R int j = 1; j <= l[i]; ++j)
		{
			str[i][j] = Get(str[i][j]);
			exi[i][str[i][j] - '0'] = true;
		}
	}
	for (R int i = n - 1; i; --i)
	{
		for (R int j = 0; j < 4; ++j)
		{
			if (exi[i + 1][j]) nex[i][j] = i + 1;
			else nex[i][j] = nex[i + 1][j];
		}
	}
	root[1] = 0, par[0] = -1, par[1] = 0; cnt = 0;
	for (R int i = 1; i <= n; ++i)
	{
		lb[i] = cnt + 1; last = root[i];
		for (R int j = 1; j <= l[i]; ++j)
		SAM::insert(i, str[i][j] - '0');
		rb[i] = cnt;
	}
	for (R int i = 1; i <= n; ++i)
	{
		for (R int j = lb[i]; j <= rb[i]; ++j)
		{
			for (R int k = 0; k < 4; ++k)
			{
				if ((!to[j][k]) && nex[i][k])
				to[j][k] = to[root[nex[i][k]]][k];
			}
		}
	}
	scanf("%d", &typ);
	if (typ == 0) printf("%d", SAM::DFS(root[1]));
	else SAM::DFS(root[1], 0), printf("%d", ans);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值