1. 程式人生 > 實用技巧 >字串雜題準備

字串雜題準備

目錄

HDU 2243 考研路茫茫——單詞情結

考點:

\(AC\)自動機+矩乘

思路:

答案是\(\sum_{i=1}^L 26^i -不合法方案\)

考慮\(L\)的值很大,而\(N\)\(len\)很小,可以由一種暴力的\(DP\)轉移想到在\(trie\)上面用矩乘實現優化。

考慮矩陣的編號就是\(trie\)上的每個點的編號,那麼轉移矩陣很容易得到,初始矩陣也容易得到,這樣不合法方案就可以用\(O(node^3*logL)\)解決。

考慮如何快速求\(\sum_{i=1}^L 26^i\),可以發現這是個多項式乘法的樣式,而且係數都為\(1\)

,考慮用倍增實現。

\(cf[i]=2^i,cf_{26}[i]=26^{2^i},val[i]=\sum_{j=1}^{2^i} 26^j\),顯然這兩個都可以快速求得。

	cf[0] = 1; fo(i, 1, 31) cf[i] = cf[i - 1] << 1;
	cf_26[0] = val[0] = 26; fo(i, 1, 30) {
		cf_26[i] = cf_26[i - 1] * cf_26[i - 1];
		val[i] = val[i - 1] + cf_26[i - 1] * val[i - 1];
	}

而後直接用倍增就可以求得\(\sum_{i=1}^L 26^i\)

即可。

#include <cstdio>
#include <vector>
#include <cstring>
#include <algorithm>
#define N 110
#define maxn 10010
#define db double
#define ll long long
#define ull unsigned ll
#define mem(x, a) memset(x, a, sizeof x)
#define mpy(x, y) memcpy(x, y, sizeof y)
#define fo(x, a, b) for (int x = (a); x <= (b); x++)
#define fd(x, a, b) for (int x = (a); x >= (b); x--)
#define go(x) for (int p = tail[x], v; p; p = e[p].fr)
using namespace std;
struct matrix{
	int n, m; ull a[N][N];
	matrix() {mem(a, 0); n = m = 0;}
	
	void clear() {mem(a, 0); n = m = 0;}
	
	matrix operator *(const matrix x) {
		matrix c; c.n = n, c.m = x.m;
		fo(k, 1, c.m) fo(i, 1, c.n) fo(j, 1, c.m)
			c.a[i][j] += a[i][k] * x.a[k][j];
		return c;
	}
}a, b, c;
ll cf[32];
int n, m, trie[maxn][27];
int fail[maxn], dl[maxn], tot = 1;
ull ans = 0, cf_26[32], val[32];
bool label[maxn];
char s[N];
vector<int> rev[maxn];

inline int read() {
	int x = 0, f = 0; char c = getchar();
	while (c < '0' || c > '9') f = (c == '-') ? 1 : f, c = getchar();
	while (c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
	return f ? -x : x;
}

void clear() {
	fo(i, 1, tot)
		rev[i].clear(), mem(trie[i], 0), label[i] = 0;
	a.clear(), b.clear(); tot = 1;
}

void insert() {
	scanf("%s", s + 1);
	int len = strlen(s + 1), now = 1;
	fo(i, 1, len) {
		int t = s[i] - 'a';
		if (! trie[now][t]) trie[now][t] = ++tot;
		now = trie[now][t];
	}
	label[now] = 1; 
}

void make_fail() {
	fo(i, 1, tot) {
		fo(j, 0, 25) {
			if (! trie[i][j]) continue;
			int res = i == 1 ? i : fail[i];
			while (res > 1 && ! trie[res][j]) res = fail[res];
			if (trie[res][j] && res != i) res = trie[res][j];
			fail[trie[i][j]] = res; rev[res].push_back(trie[i][j]);
		}
	}
	int l = 0, r = 0;
	fo(i, 1, tot) if (label[i]) dl[++r] = i;
	while (l++ < r) {
		int x = dl[l];
		fo(i, 0, 25) {
			if (! trie[x][i] || label[trie[x][i]]) continue;
			label[trie[x][i]] = 1, dl[++r] = trie[x][i];
		}
		int len_ = rev[x].size() - 1;
		fo(i, 0, len_) {
			if (label[rev[x][i]]) continue;
			label[rev[x][i]] = 1, dl[++r] = rev[x][i];
		}
	}
}

bool check() {
	ull res = 1, s = 0;
	fo(i, 1, m) res = res * 26, s += res;
	return s == ans;
}

void solve(int x) {
	ll now = 0; ull kk = 1; ans = 0;
	fd(i, 31, 0) if (now + cf[i] <= x)
		ans += kk * val[i], now += cf[i], kk = kk * cf_26[i];
	/*
	if (! check()) {
		printf("wrong\n");
	}
	*/
}

int main() {
	freopen("word.in", "r", stdin);
	freopen("word.out", "w", stdout);
	cf[0] = 1; fo(i, 1, 31) cf[i] = cf[i - 1] << 1;
	cf_26[0] = val[0] = 26; fo(i, 1, 30) {
		cf_26[i] = cf_26[i - 1] * cf_26[i - 1];
		val[i] = val[i - 1] + cf_26[i - 1] * val[i - 1];
	}
	while (scanf("%d%d", &n, &m) != EOF) {
		
		clear();
		
		solve(m);
		
		fo(i, 1, n) insert();
		
		make_fail();
		
		a.n = 1, a.m = tot + 1;
		fo(i, 0, 25) {
			if (trie[1][i]) {
				if (! label[trie[1][i]])
					a.a[1][trie[1][i]]++;
			}
			else a.a[1][1]++;
		}
		
		b.n = b.m = tot + 1, b.a[b.n][b.m] = 1;
		
		fo(i, 1, tot) {
			if (label[i]) continue;
			b.a[i][tot + 1] = 1;
			fo(j, 0, 25) {
				int now = i;
				while (now > 1 && ! trie[now][j])
					now = fail[now];
				if (trie[now][j]) now = trie[now][j];
				if (label[now]) continue;
				b.a[i][now]++;
			}
		}
		while (m) {
			if (m & 1) a = a * b;
			b = b * b, m >>= 1;
		}
//		printf("%llu ", ans);
		printf("%llu\n", ans - a.a[1][tot + 1]);
	}
	return 0;
}