【问题标题】:Optimal algorithm to count the number of strings a DFA accepts计算 DFA 接受的字符串数量的最佳算法
【发布时间】:2016-01-08 13:48:52
【问题描述】:

这是我遇到的问题

确定性有限自动机 (DFA) 是一种有限状态机,它接受/拒绝有限的符号字符串,并且只为每个输入字符串生成唯一的自动化计算(或运行)。

DFA 可以使用状态图来表示。例如,在下图所示的自动机中,存在三种状态:S0、S1 和 S2(用圆圈图形表示)。自动机将 0 和 1 的有限序列作为输入。对于每个状态,都有一个过渡箭头指向 0 和 1 的下一个状态。读取一个符号后,DFA 会按照过渡箭头确定性地从一个状态跳转到另一个状态。例如,如果自动机当前处于状态 S0 并且当前输入符号为 1,则它确定性地跳转到状态 S1。 DFA 具有计算开始的起始状态(以图形方式由从无到有的箭头表示)和一组接受状态(以图形方式由双圆圈表示),这些状态有助于定义计算何时成功。

这些是 DFA 接受的一些字符串,

0
00
000
11
110
1001

您在输入中得到一个 DFA 和一个整数 N。您必须知道给定 DFA 接受多少个长度为 N 的不同字符串。

注意事项

  • 假设每个状态都有两个出边(一个代表 0,一个代表 1)。两个传出边不会进入相同的状态。
  • 可能有多个接受状态,但只有一个开始状态。
  • 开始状态也可以是接受状态。

输入格式

  • 状态从 0 到 K-1 编号,其中 K 是 DFA 中的状态总数。
  • 给您三个数组 A、B、C 和两个整数 D 和 N。
  • 数组 A 表示从状态编号 i 到状态 A[i] 的 0 边,对于所有 0 ≤ i ≤ K-1
  • 数组 B 表示从状态编号 i 到状态 B[i] 的第 1 条边,对于所有 0 ≤ i ≤ K-1
  • 数组 C 包含所有接受状态的索引。
  • 整数 D 表示开始状态。
  • 整数 N 表示您必须计算给定 DFA 接受多少个长度为 N 的不同字符串。

约束

1 ≤ K ≤ 50 
1 ≤ N ≤ 10^4

例子:

对于图中显示的 DFA,输入为

A = [0, 2, 1]
B = [1, 0, 2]
C = [0]
D = 0

输入 1

N = 2
Strings '00' and '11' are only strings on length 2 which are accepted. So, answer is 2.

输入 2

N = 1
String '0' is the only string. Answer is 1.

我的解决方案

我在 Mind 中有一个蛮力递归解决方案,其工作原理如下:

  1. 从开始状态开始。让它成为curr
  2. 检查N==0curr 是否为接受状态,然后将1 增加到总状态并返回;
  3. 现在将0 and 1 作为输入,让curr 状态变为curr0curr1。使用curr 状态为curr0curr1 以及N-1 调用递归函数两次;

我的解决方案有问题

但问题是这个解决方案将检查所有可能的长度为N 的字符串,其中包含{0,1}。所以它的时间复杂度是2^N,因为1 <= N <= 10^4这是指数级的,不可行。

问题

有人可以建议这个问题的有效解决方案吗?可能这个问题是 NP-Complete 的,这是唯一的解决方案。任何帮助将不胜感激。

【问题讨论】:

  • 出边能否“跳过”状态(例如,S0 直接到 S2)?
  • 是的,它可以从任何状态转到任何其他状态

标签: algorithm data-structures np-complete


【解决方案1】:

您的解决方案中的想法很好。问题是它可以在完全相同的状态和 N 上进行许多递归调用。如果 0 和 1 都将起始状态带到相同的新状态,您可以看到一个简单的示例,在该状态下它将在两次新状态。

此属性是可以使用动态编程和/或记忆化改进的算法的标志。根据您与谁交谈,这两种技术要么是同一件事,要么是近亲。无论哪种方式,他们都确保任何给定呼叫的工作只完成一次,即使稍后出现相同的呼叫。 (相同的呼叫可以产生与原始呼叫相同的答案。)

为此,我们需要跟踪已进行了哪些调用,即已计算了 (State, Length) 的哪些组合。我们可以将这些答案保存在表格中。

首先初始化表中所有 Length=0 的点。如果状态是接受状态,则用 1 填充该位置;如果状态不是接受状态,则用 0 填充该点。现在循环 K 从 1 到 N。对于每个 K,循环遍历所有状态 S。用 Table[S0,K-1 填充 Table[S,K] ] + Table[S1,K-1],其中 S0 是状态 S 在输入 0 时转换到的状态,S1 在输入 1 时转换到的状态。

最后,从 Table[StartState,N] 中读出答案。

【讨论】:

  • 嘿,谢谢。我认为这应该有效。我还考虑过一种动态规划算法,但主要是考虑是否可以计算一个矩阵 A[K][K],其中包含所有状态对之间的路径长度。
【解决方案2】:

DP解决方案

public static int automata(ArrayList<Integer> a, ArrayList<Integer> b,
        ArrayList<Integer> c, int D, int n) {
    HashMap<Integer, Integer> map = new HashMap<>();
    int[][] table = new int[a.size()][n + 1];
    for (int i = 0; i < c.size(); i++) {
        map.put(c.get(i), 1);
    }
    for (int i = 0; i < a.size(); i++) {
        if (map.containsKey(i))
            table[i][0] = 1;
    }
    for (int i = 1; i <= n; i++) {
        for (int j = 0; j < a.size(); j++) {
            table[j][i] = table[a.get(j)][i - 1] + table[b.get(j)][i - 1];
        }
    }

    return table[D][n];
}

【讨论】:

    【解决方案3】:

    只是一个小的修改,这可以在 O(n) 空间中解决,因为时间 n 的状态仅取决于时间 n-1。代码如下:

    int main()
    {
        int k;
        cin >> k;
        vector<int> A(k);
        vector<int> B(k);
        vector<int> C;
    
        int val;
    
        for(int i = 0; i < k; ++i) cin >> A[i];
        for(int i = 0; i < k; ++i) cin >> B[i];
        for(int i = 0; i < k; ++i) 
        {
            cin >> val;
            if(val) C.push_back(i);
        }
    
        int D,N;
        cin >> D >> N;
    
    
        //For memoize
        // vector<vector<int>> dp(k, vector<int>(k,-1));
    
    
        // cout << rec(N, D, A, B, accepting_states) << endl;
        // cout << memoize(N, D, A, B, accepting_states, dp) << endl;
    
        vector<int> before(k, 0);
        vector<int> after(k);
    
        for(int ele:C) before[ele] = 1;
    
        for(int t = 1; t <= N; ++t)
        {
            for(int s = 0; s < k; ++s)
            {
                after[s] = before[A[s]] + before[B[s]];
            }
            before = after;
        } 
    
        cout << after[D] << endl;
    
    
    }
    

    【讨论】:

      猜你喜欢
      • 2014-08-05
      • 2017-09-06
      • 2015-05-02
      • 1970-01-01
      • 2021-12-16
      • 2019-04-21
      • 1970-01-01
      • 1970-01-01
      • 2017-10-20
      相关资源
      最近更新 更多