Algorithm 计算DFA接受的字符串数的最佳算法

Algorithm 计算DFA接受的字符串数的最佳算法,algorithm,data-structures,np-complete,Algorithm,Data Structures,Np Complete,这是我遇到的问题 确定性有限自动机(DFA)是一种有限状态机,它接受/拒绝有限的符号字符串,并且只为每个输入字符串生成唯一的自动化计算(或运行) DFA可以使用状态图表示。例如,在下图所示的自动机中,有三种状态:S0、S1和S2(用圆圈图形表示)。自动机采用0和1的有限序列作为输入。对于每个状态,都有一个过渡箭头,指向0和1的下一个状态。在读取符号时,DFA通过跟随转换箭头,从一个状态确定地跳到另一个状态。例如,如果自动机当前处于状态S0,且当前输入符号为1,则它确定地跳到状态S1。DFA有一个

这是我遇到的问题

确定性有限自动机(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
  • 对于所有0,数组A表示从编号为i的状态到状态A[i]的0边≤ 我≤ K-1
  • 对于所有0,数组B表示从状态编号i到状态B[i]的1边≤ 我≤ 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 输入2 我的解决方案 我想到了一个蛮力递归解决方案,其工作原理如下:

  • 从开始状态开始。让它成为当前的
    curr
  • 检查
    N==0
    curr
    是否为接受状态,然后将
    1
    增加到总状态并返回
  • 现在,对于作为输入的
    0和
    1,让
    curr
    状态变为
    curr0
    curr1
    。使用
    curr
    状态为
    curr0
    curr1
    以及
    N-1
    调用递归函数两次 我的解决方案有问题吗
    但问题是,此解决方案将检查所有可能的长度为
    N
    的字符串,其中包含
    {0,1}
    。因此,这个问题的时间复杂度将是
    2^N
    ,因为
    1您的解决方案中的想法很好。问题是它可以对完全相同的状态和N进行多次递归调用。如果0和1都将开始状态转换为相同的新状态,则可以看到一个简单的例子,其中它将对新状态进行两次相同的调用

    此属性是可使用动态规划和/或记忆改进的算法的特征。根据你和谁交谈,这两种技巧要么是相同的,要么是近亲。无论哪种方式,它们都确保任何给定调用的工作只完成一次,即使稍后出现相同的调用。(相同的呼叫只能产生与原始呼叫相同的应答。)

    要做到这一点,我们需要跟踪进行了哪些调用,即计算了哪些(状态、长度)组合。我们可以把这些答案放在一张桌子里

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

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

    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];
    }
    
    公共静态int自动机(ArrayList a、ArrayList b、,
    数组列表c,int D,int n){
    HashMap=newHashMap();
    int[]table=newint[a.size()][n+1];
    对于(int i=0;i对于(int i=1;i只是一个小的修改,这可以在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;
    
    
    }
    
    intmain()
    {
    int k;
    cin>>k;
    向量A(k);
    载体B(k);
    载体C;
    int-val;
    对于(inti=0;i>A[i];
    对于(inti=0;i>B[i];
    对于(int i=0;i>val;
    如果(val)C.向后推(i);
    }
    int D,N;
    cin>>D>>N;
    //回忆录
    //向量dp(k,向量(k,-1));
    
    //一个输出边能“跳过”状态吗(例如,S0直接跳到S2)?是的,它可以从任何状态跳到任何其他状态。谢谢。我认为这应该能工作。我也考虑过动态规划算法,但主要考虑的是我是否能计算出一个矩阵a[K][K],其中包含所有状态对之间的路径长度。
    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];
    }
    
    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;
    
    
    }