您的位置:首页 > 其它

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 zoj sgu dp