Non Absorbing DFA DP (ASC2A SGU201 ZOJ2337 ACdream1218 Gym100197A)
2016-08-20 15:48
246 查看
SGU 201 Non Absorbing DFA DP
( ZOJ 2337 、ACdream 1218 、Gym 100197A)Andrew Stankevich Contest 2 A题
题意
给定一个DFA(有字符集、初始状态、终态集,每个状态遇到每个字母会转换到哪个状态)。问某个长度的字符串有多少种可以被该DFA接受(从初始状态出发,根据字符串的每个字母进行转移,最后停留在终态)。但这个DFA有个特殊的地方,某些状态遇到某些字母进行转移的时候,下次转移时仍然使用上一个字母,而不是把上个字母移除之后使用下个字母进行转移。解法
先对每个状态和字母的组合进行DFS,搜到最终转移到的状态,然后进行DP,dp[i][j]表示串长为i,停留在j状态的字符串数目,对于每个dp[i][j],枚举遇到的下一个字母,如果j状态遇到该字母能够转移到nxt状态而不是陷入死循环,则dp[i+1][nxt]+=dp[i][j]。最终ans=∑dp[m][i],i∈终态AC代码
(SGU 201 内存391KB 用时46 ms 编译器Visual Studio C++ 2010)#include<cstring> #include<cstdio> using namespace std; const int N = 1010; ///最大状态个数 int nextSta [26]; ///每个状态在某个字母下的下一个状态 bool notRemove [26]; ///为true代表该状态使用该字母进行转移后,下次仍然使用该字母,该字母不移除 int zt ; ///终态 int staCnt,alphaCnt,start,ztNum,cc; ///状态数、字母数、初始状态、终态数,串长 ///大整形类,支持加法 struct BigInt { static const int DIGITAL_PER_LL = 18;///一个long long保存18个十进制位 static const int MAX_LEN = 85 / DIGITAL_PER_LL + 1; ///根据最大的十进制位数,计算出需要的long long个数 long long dat[MAX_LEN]; /// +=运算 BigInt& operator+=(const BigInt& other) { for(int i = 0;i < MAX_LEN;++i){ dat[i] += other.dat[i]; if(dat[i] >= 1000000000000000000LL){ dat[i] -= 1000000000000000000LL; dat[i + 1]++; } } return *this; } ///输出 void print() { ///去前导零 int start = MAX_LEN - 1; while(start > 0 && dat[start] == 0) { start--; } ///第一个long long无前导零,其它每个18位,有前导零 printf("%I64d",dat[start--]); while(start >= 0) { printf("%.18I64d",dat[start--]); } putchar('\n'); } ///赋值 void setValue(long long num) { dat[0] = num; for(int i = 1; i < MAX_LEN; ++i) { dat[i] = 0; } } ///初始化为0 BigInt() { setValue(0); } }; BigInt dp[2] ;///dp[i][j]表示串长为i,停留在j状态的字符串数目 ///输入 void input() { char s[100]; scanf("%s",s); alphaCnt = strlen(s); scanf("%d%d%d",&staCnt,&start,&ztNum); for(int i = 0; i < ztNum; ++i) { scanf("%d",&zt[i]); } for(int i = 0; i < staCnt; ++i) { for(int j = 0; j < alphaCnt; ++j) { scanf("%d",&nextSta[i][j]); nextSta[i][j]--; } } for(int i = 0; i < staCnt; ++i) { for(int j = 0; j < alphaCnt; ++j) { int t; scanf("%d",&t); notRemove[i][j] = t == 1; } } scanf("%d",&cc); } ///状态访问标志 bool visit ; ///更新每个状态遇到每个字母最终会到达何状态,死循环则为-1 int dfs(int state,int ch) { visit[state] = true; int nxt = nextSta[state][ch]; if(!notRemove[state][ch]) {///访问到了一个可以移除该字母的状态,结束 notRemove[state][ch] = false; return nextSta[state][ch] = nxt; } else if(visit[nxt]) { ///访问到了一个已访问的状态,死循环 notRemove[state][ch] = false; return nextSta[state][ch] = -1; } notRemove[state][ch] = false; ///取消该标记,下次不用重复搜了 return nextSta[state][ch] = dfs(nxt,ch); } ///预处理,调用dfs函数更新nextSta数组 void init() { for(int i = 0; i < staCnt; ++i) { for(int j = 0; j < alphaCnt; ++j) { if(notRemove[i][j]) { memset(visit,0,sizeof(visit)); dfs(i,j); } } } } ///计算答案 BigInt cal() { for(int i = 0; i < staCnt; ++i) { dp[0][i].setValue(0); } dp[0][start - 1].setValue(1);///初始dp[0][初始状态]=1,其它为0 bool flag = true; for(int i = 0; i < cc; ++i) { for(int i = 0; i < staCnt; ++i) { dp[flag][i].setValue(0); } for(int st = 0; st < staCnt; ++st) { for(int zm = 0; zm < alphaCnt; ++zm) { int nxt = nextSta[st][zm];///st状态碰到zm字母最终到达的状态 if(nxt != -1) {///不会死循环 dp[flag][nxt] += dp[!flag][st];///更新到达的状态计算 } } } flag = !flag; } ///统计停留在终态的情况 BigInt res; for(int i = 0; i < ztNum; ++i) { res += dp[!flag][zt[i] - 1]; } return res; } int main() { input(); init(); cal().print(); return 0; }
相关文章推荐
- ASC码表
- 探讨?C#中如何得到双字节字符的ASCII码 C# asc() 和vb
- javascript技巧(判断全角,查看字符的asc码,判断是否是数字)
- 快速排序(ASC)
- oracle查询排序asc/desc 多列 order by
- HDOJ2000(ASC||码排序)【sort函数】
- 键盘按键对应ASCⅡ码(JS 中会用到)
- Asc码排序,冒泡排序
- Android COLLATE LOCALIZED ASC
- 汇编-ASCⅡ码转二进制码
- r8 - ASC 41(俄罗斯多校)
- ASC(22)H(大数+推公式)
- oracle查询排序asc/desc 多列 order by
- aac_adtstoasc bitstream filter
- Andrew Stankevich Contest 2 (ASC 2) |我为什么要开这套题
- asc 点阵数组和计算认识
- ASCⅡ码表
- [tamarin系列之3] ASC编译器命令行基础
- javascript技巧(判断全角,查看字符的asc码,判断是否是数字)