SPOJ 1676 矩阵乘法+DP

题意:

给定N (1 ≤ N ≤ 10)个长度不超过6的单词,求由大写字母组成长度为L的包含至少一个给定单词的字符串有多少种,答案 mod 10007,(1 ≤ L ≤ 10^6)。

 

题解:

这个题最早是在一个关于trie图的论文中看到了,最近jzh又讲到了这个题,于是就把它做了~

大致有两种做法,两种方法都需要矩阵乘法加速

1、trie图中的dp

2、直接人工减少转移数量

具体做法点击这里

 

大致思路就是将不可能构成单词的前缀合成一类,然后胡搞就行了。

 

View Code
  1 #include <iostream>
  2 #include <cstring>
  3 #include <cstdio>
  4 #include <string>
  5 #include <cstdlib>
  6 #include <algorithm>
  7 #include <map>
  8 
  9 #define N 60
 10 #define SIZE 70
 11 #define mod 10007
 12 
 13 using namespace std;
 14 
 15 map<string,int> mp;
 16 
 17 int n,cnt,res,m;
 18 string str[N],prefix[SIZE];
 19 
 20 struct MT
 21 {
 22     int x,y;
 23     int mt[SIZE][SIZE];
 24 }zy,ans;
 25 
 26 inline MT operator *(MT a,MT b)
 27 {
 28     MT c; memset(c.mt,0,sizeof c.mt);
 29     c.x=a.x; c.y=b.y;
 30     for(int i=1;i<=c.x;i++)
 31         for(int j=1;j<=c.y;j++)
 32             for(int k=1;k<=a.y;k++)
 33             {
 34                 c.mt[i][j]+=a.mt[i][k]*b.mt[k][j];
 35                 if(c.mt[i][j]>=mod) c.mt[i][j]%=mod;
 36             }
 37     return c;
 38 }
 39 
 40 inline void read()
 41 {
 42     mp.clear();
 43     for(int i=1;i<=n;i++)
 44     {
 45         cin>>str[i];
 46         mp[str[i]]=520;
 47     }
 48 }
 49 
 50 inline bool check(string x)//检查x是否是合法的前缀 
 51 {
 52     string::size_type pos;
 53     for(int i=1;i<=n;i++)
 54     {
 55         pos=x.find(str[i]);
 56         if(pos!=x.npos) return false;
 57     }
 58     return true;
 59 }
 60 
 61 inline void get_det()
 62 {
 63     memset(zy.mt,0,sizeof zy.mt);
 64     cnt=0;
 65     for(int i=1;i<=n;i++)
 66     {
 67         string tmp;
 68         for(int j=0;j<str[i].length();j++)
 69         {
 70             tmp.push_back(str[i][j]);
 71             if(check(tmp)&&mp[tmp]==0)
 72             {
 73                 mp[tmp]=++cnt;//前缀字符串的映射 
 74                 prefix[cnt]=tmp;//前缀字符串 
 75             }
 76         }
 77     }
 78     zy.x=zy.y=cnt+1;
 79     
 80     string tmp;
 81     for(int i=1;i<=cnt;i++)
 82         for(int j=0;j<26;j++)
 83         {
 84             tmp=prefix[i]; tmp.push_back(j+'A');
 85             for(int k=tmp.length();k>=0;k--)
 86             {
 87                 if(k==0)
 88                 {
 89                     zy.mt[cnt+1][mp[prefix[i]]]++;//不存在后缀是已知的前缀 
 90                     break;
 91                 }
 92                 else 
 93                 {
 94                     string tp;
 95                     for(int p=tmp.length()-k;p<tmp.length();p++)
 96                         tp.push_back(tmp[p]);
 97                     if(mp[tp]==520) break;//出现单词,不合法 
 98                     else if(mp[tp]!=0) {zy.mt[mp[tp]][mp[prefix[i]]]++;break;}//存在最大的后缀是已知的前缀 
 99                 }
100             }
101         }
102     for(int i=0;i<26;i++)
103     {
104         string sy;
105         sy.push_back(i+'A');
106         if(mp[sy]==0) zy.mt[cnt+1][cnt+1]++;
107         else if(mp[sy]==520) continue;
108         else zy.mt[mp[sy]][cnt+1]++;
109     }
110     
111     ans.x=cnt+1; ans.y=1;
112     memset(ans.mt,0,sizeof ans.mt);
113     ans.mt[cnt+1][1]=1;
114 }
115 
116 inline int qs(int a,int b)
117 {
118     int res=1;
119     while(b)
120     {
121         if(b&1) res=(res*a)%mod;
122         a=(a*a)%mod;
123         b>>=1;
124     }
125     return res;
126 }
127 
128 inline void go()
129 {
130     get_det();
131     res=qs(26,m);
132     while(m)
133     {
134         if(m&1) ans=zy*ans;
135         zy=zy*zy;
136         m>>=1;
137     }
138     
139     int tmp=0;
140     for(int i=1;i<=cnt+1;i++) tmp=(tmp+ans.mt[i][1])%mod;
141     res-=tmp;
142     printf("%d\n",(res+mod)%mod);
143 }
144 
145 int main()
146 {
147     while(scanf("%d%d",&n,&m)!=EOF) read(),go();
148     return 0;
149 }

 

 

posted @ 2013-02-17 12:49  proverbs  阅读(520)  评论(0编辑  收藏  举报