1. 程式人生 > >HDU 2243 考研路茫茫——單詞情結 ( Trie圖 && DP && 矩陣構造冪和 )

HDU 2243 考研路茫茫——單詞情結 ( Trie圖 && DP && 矩陣構造冪和 )

amp view cout memset 種類 string spl nbsp 小寫字母

題意 : 長度不超過L,只由小寫字母組成的,至少包含一個詞根的單詞,一共可能有多少個呢?這裏就不考慮單詞是否有實際意義。

比如一共有2個詞根 aa 和 ab ,則可能存在104個長度不超過3的單詞,分別為
(2個) aa,ab,
(26個)aaa,aab,aac...aaz,
(26個)aba,abb,abc...abz,
(25個)baa,caa,daa...zaa,
(25個)bab,cab,dab...zab。

分析 : 這題和 POJ 2778 非常相似,如果沒有做過 POJ 2778 建議先去搞定那道題。此題難點就在於這個是要求不超過 L 長度包含詞根的單詞,根據解決 POJ 2778 的經驗,我們可以得出 答案 = 總單詞種類數 - 不包含詞根的單詞數。首先總單詞數可以很容易想到為 261

+ 262 + 263 + ..... + 26L ,而不包含詞根的單詞總數可以這樣得到 ==> 假設原 Trie 圖構建出來的狀態矩陣為 A ,那麽同樣的我們需要構造一個冪和即 A1 + A2 + A3 + ..... + AL 然後最後的答案便是 ∑AL(0, i) ( i ∈ 1~矩陣長度 ) ,那怎麽去構造這兩個冪和呢?

技術分享

只要利用這個公式即可,用原矩陣 + 單位矩陣 + 零矩陣構造出新矩陣,最後右上角的矩陣便是冪和的矩陣!

這裏還有一點要註意,就是對於 2^64次方求模有一個很巧的方法,也就是直接定義為 unsigned long long (範圍 : 0 ~ 2^64 -1),溢出就相當於求模了!

技術分享
#include<string.h>
#include<stdio.h>
#include<iostream>
#include<queue>
#define ULL unsigned long long
using namespace std;

const int Max_Tot = 1e2 + 10;
const int Letter  = 26;
int maxn;///矩陣的大小
char S[11];

struct mat{ ULL m[111][111]; }unit, M;
mat operator * (mat a, mat b){
    mat ret;
    
for(int i=0; i<maxn; i++){ for(int j=0; j<maxn; j++){ ret.m[i][j] = (ULL)0; for(int k=0; k<maxn; k++){ ret.m[i][j] += a.m[i][k]*b.m[k][j]; } } } return ret; } inline void init_unit() { for(int i=0; i<maxn; i++) unit.m[i][i] = 1; } mat pow_mat(mat a, long long n){ mat ret = unit; while(n){ if(n&1) ret = ret * a; a = a*a; n >>= 1; } return ret; } struct Aho{ struct StateTable{ int Next[Letter]; int fail, flag; }Node[Max_Tot]; int Size; queue<int> que; inline void init(){ while(!que.empty()) que.pop(); memset(Node[0].Next, 0, sizeof(Node[0].Next)); Node[0].fail = Node[0].flag = 0; Size = 1; } inline void insert(char *s){ int now = 0; for(int i=0; s[i]; i++){ int idx = s[i] - a; if(!Node[now].Next[idx]){ memset(Node[Size].Next, 0, sizeof(Node[Size].Next)); Node[Size].fail = Node[Size].flag = 0; Node[now].Next[idx] = Size++; } now = Node[now].Next[idx]; } Node[now].flag = 1; } inline void BuildFail(){ Node[0].fail = -1; for(int i=0; i<Letter; i++){ if(Node[0].Next[i]){ Node[Node[0].Next[i]].fail = 0; que.push(Node[0].Next[i]); }else Node[0].Next[i] = 0;///必定指向根節點 } while(!que.empty()){ int top = que.front(); que.pop(); if(Node[Node[top].fail].flag) Node[top].flag = 1; for(int i=0; i<Letter; i++){ int &v = Node[top].Next[i]; if(v){ que.push(v); Node[v].fail = Node[Node[top].fail].Next[i]; }else v = Node[Node[top].fail].Next[i]; } } } inline void BuildMatrix(){ for(int i=0; i<Size; i++) for(int j=0; j<Size; j++) M.m[i][j] = 0; for(int i=0; i<Size; i++){ for(int j=0; j<Letter; j++){ if(!Node[i].flag && !Node[ Node[i].Next[j] ].flag) M.m[i][Node[i].Next[j]]++; } } maxn = Size; } }ac; ULL GetSum(long long num){ mat ret; ret.m[0][0] = 26; ret.m[0][1] = 1; ret.m[1][0] = 0; ret.m[1][1] = 1; int tmp = maxn; maxn = 2; ret = pow_mat(ret, ++num); maxn = tmp; return ret.m[0][1]-1; } ULL GetElimination(long long num){ mat tmp; for(int i=0; i<maxn; i++)///左上角 為 原矩陣 for(int j=0; j<maxn; j++) tmp.m[i][j] = M.m[i][j]; for(int i=0; i<maxn; i++)///右上角 為 單位矩陣 for(int j=maxn; j<(maxn<<1); j++) tmp.m[i][j] = (i+maxn == j); for(int i=maxn; i<(maxn<<1); i++)///左下角 為 零矩陣 for(int j=0; j<maxn; j++) tmp.m[i][j] = 0; for(int i=maxn; i<(maxn<<1); i++)///右下角 為 單位矩陣 for(int j=maxn; j<(maxn<<1); j++) tmp.m[i][j] = (i==j); int Temp = maxn; maxn <<= 1;///先將原本矩陣的大小放大一倍進行快速冪運算,這個和我快速冪的寫法有關 tmp = pow_mat(tmp, ++num); ULL ret = (ULL)0; maxn = Temp;///再回復成原來大小 for(int i=maxn; i<(maxn<<1); i++)///右上角的矩陣就是冪和了 ret += tmp.m[0][i]; return (--ret);///需要 -1 } int main(void) { int n, m; while(~scanf("%d %d", &m, &n)){ ac.init(); for(int i=0; i<m; i++){ scanf("%s", S); ac.insert(S); } ac.BuildFail(); ac.BuildMatrix(); init_unit(); ULL Tot = GetSum((long long)n);///註意是傳long long不然會爆int ULL Elimination = GetElimination((long long)n); cout<<Tot-Elimination<<endl; } return 0; }
View Code

HDU 2243 考研路茫茫——單詞情結 ( Trie圖 && DP && 矩陣構造冪和 )