[HDU2243]考研路茫茫——單詞情結

題目

傳送門 to HDU

傳送門 to VJ

思路

考慮一位一位的加入字符,我們只在乎 當前字符串末尾的匹配情況

既然如此,我們用 ACAC 自動機上的一個節點來表示狀態就可以了。

現在是一個 dpdp 轉移。似乎要存儲是否曾經出現過詞根?

其實不用,只需要用所有情況減去一次都沒有出現過的情況即可。

向量中加入一個值,用來求累和——就不需要考慮不轉移的情況了。

並且是常係數。所以使用矩陣加速。複雜度 O(n6logL)\mathcal O(n^6\log L)

代碼

重大坑點(我因此 WAWA 了很多次):一個節點是否爲“沒有出現過詞根”,要檢查其 failfail 指針,而不是單純地考慮當前這個點是否爲單詞末尾。詳見代碼 6464 行。

#include <cstdio>
#include <iostream>
#include <vector>
#include <cstring>
using namespace std;
inline int readint(){
	int a = 0, f = 1; char c = getchar();
	for(; c<'0'||c>'9'; c=getchar())
		if(c == '-') f = -f;
	for(; '0'<=c&&c<='9'; c=getchar())
		a = (a<<3)+(a<<1)+(c^48);
	return a*f;
}
inline void writeint(long long x){
	if(x < 0) putchar('-'), x = -x;
	if(x > 9) writeint(x/10);
	putchar((x%10)^48);
}

# define MB template < typename T >
MB void getMax(T &a,const T &b){ if(a < b) a = b; }
MB void getMin(T &a,const T &b){ if(b < a) a = b; }

const int MaxN = 27;

namespace ACmachine{ // 自動AC機 [\doge]
	struct Node{
		int son[26], fail, val;
	} node[MaxN];
	int cntNode;
	int newNode(){
		int &id = ++ cntNode;
		memset(node[id].son,0,26<<2);
		node[id].fail = node[id].val = 0;
		return id;
	}
	void clear(){ cntNode = -1; newNode(); }

	void insert(char s[],int val){
		int o = 0;
		for(int i=0; s[i]!='\0'; ++i){
			int &d = node[o].son[s[i]-'a'];
			if(d == 0) d = newNode(); o = d;
		}
		node[o].val = val;
	}

	int q[MaxN];
	void build(){
		int *head = q, *tail = q;
		for(int i=0; i<26; ++i)
			if(node[0].son[i] != 0)
				*(tail ++) = node[0].son[i];
		while(head != tail){
			int o = *(head ++);
			for(int i=0; i<26; ++i){
				int d = node[node[o].fail].son[i];
				if(node[o].son[i] != 0){
					node[node[o].son[i]].fail = d;
					*(tail ++) = node[o].son[i];
				} else node[o].son[i] = d;
			}
	// 重大坑點!這個點即使不是直接的末尾,實際上也匹配完成了!
			getMax(node[o].val,node[node[o].fail].val);
	// 重大坑點!這個點即使不是直接的末尾,實際上也匹配完成了!
		}
	}
} using namespace ACmachine;

typedef unsigned long long int_;
struct Matrix{
	int_ a[MaxN][MaxN];
	void clear(){ memset(a,0,MaxN*MaxN<<3); }
	Matrix operator * (const Matrix &b) const {
		Matrix c; c.clear();
		for(int i=0; i<MaxN; ++i)
			for(int k=0; k<MaxN; ++k)
				for(int j=0; j<MaxN; ++j)
					c.a[i][j] += a[i][k]*b.a[k][j];
		return c;
	}
	Matrix &operator *= (const Matrix &b){
		return *this = (*this)*b;
	}
};
Matrix qkpow(Matrix base,int_ q){
	Matrix ans = base; -- q;
	for(; q; q>>=1,base*=base)
		if(q&1) ans *= base;
	return ans;
}

int_ dengbi(int_ base,int q){
	int_ ans = 0, mi = 1;
	for(int i=30; ~i; --i){
		ans *= mi+1, mi *= mi;
		if(q>>i&1)
			ans += (mi *= base);
	}
	return ans;
}

char s[10]; int n, L;
Matrix movement, origin;

int main(){
	while(cin >> n >> L){
		clear(), movement.clear();
		for(int i=1; i<=n; ++i)
			cin >> s, insert(s,i);
		build(), origin.clear();
		for(int i=0; i<=cntNode; ++i){
			if(node[i].val > 0) continue;
			for(int j=0; j<26; ++j)
				movement.a[i][node[i].son[j]] ++;
		}
		for(int i=0; i<=cntNode; ++i)
			if(node[i].val == 0)
				movement.a[i][MaxN-1] = -1; // 統計答案
		movement.a[MaxN-1][MaxN-1] = 1; // 求累加
		origin.a[0][0] = 1; // 從 null 出發
		origin.a[0][MaxN-1] = dengbi(26,L)+1;
		origin *= qkpow(movement,int_(L)+1ull);
		cout << origin.a[0][MaxN-1] << endl;
	}
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章