状态机模型
概述
状态机是有限状态自动机的简称,是现实事物运行规则抽象而成的一个数学模型。
- 以下关于状态机介绍的内容参考自知乎:文章链接
- 先来解释什么是“状态”( State )。现实事物是有不同状态的,例如一个自动门,就有 open 和 closed 两种状态。我们通常所说的状态机是有限状态机,也就是被描述的事物的状态的数量是有限个,例如自动门的状态就是两个 open 和 closed 。
- 状态机,也就是 State Machine ,不是指一台实际机器,而是指一个数学模型。说白了,一般就是指一张状态转换图。例如,根据自动门的运行规则,我们可以抽象出下面这么一个图。
- 自动门有两个状态,open 和 closed ,closed 状态下,如果读取开门信号,那么状态就会切换为 open 。open 状态下如果读取关门信号,状态就会切换为 closed 。
- 状态机的全称是有限状态自动机,自动两个字也是包含重要含义的。给定一个状态机,同时给定它的当前状态以及输入,那么输出状态时可以明确的运算出来的。例如对于自动门,给定初始状态 closed ,给定输入“开门”,那么下一个状态时可以运算出来的。
- 这样状态机的基本定义我们就介绍完毕了。重复一下:状态机是有限状态自动机的简称,是现实事物运行规则抽象而成的一个数学模型。
状态机可归纳为4个要素,即现态、条件、动作、次态。这样的归纳,主要是出于对状态机的内在因果关系的考虑。“现态”和“条件”是因,“动作”和“次态”是果。
- 详解如下:
- 现态:是指当前所处的状态。
- 条件:又称为“事件”,当一个条件被满足,将会触发一个动作,或者执行一次状态的迁移。
- 动作:条件满足后执行的动作,动作执行完毕后,可以迁移到新的状态,也可以仍旧保持原状态。动作不是必需的,当条件满足后,也可以不执行任何动作,直接迁移到新状态。
- 次态:条件满足后要迁往的新状态。“次态”是相对于“现态”而言的,“次态”一旦被激活,就转变成新的“现态”了。
状态机模型例题
198. 打家劫舍
188. 买卖股票的最佳时机 IV
309. 最佳买卖股票时机含冷冻期
设计密码
- 状态定义:
- dp[i][j]:写到密码串前 i 个字母,且当前跳到 kmp 的第 j 个状态的方案数量
- 状态转移方程:
dp[i + 1][u] = dp[i + 1][u] + dp[i][j]
。这里的状态定义是已经有的长度,不包括当前枚举的字母。 也可以理解为原来的 dp[i][j] = dp[i-1][j] 这类的,都是拿后继更新前驱。然而本题是用当前这个后继状态来更新它能到达的所有后继状态。 - Java实现代码如下:
import java.util.*;
public class Main {
static int N = 55;
static int MOD = (int) (1e9+7);
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
int n = 0;
String s = "";
char[] str = new char[26];
n = in.nextInt();
in.nextLine();
s = in.nextLine();
str = s.toCharArray();
System.out.println(solve(n, str));
in.close();
}
private static int solve(int n, char[] str) {
int len = str.length;
int[] next = new int[len];
int[][] dp = new int[N][N];
// 求next数组
for (int i = 2, j = 0; i < len; i++) {
while (j > 0 && str[i-1] != str[j]) {
j = next[j];
}
if (str[i-1] == str[j]) {
j++;
}
next[i] = j;
}
dp[0][0] = 1; // 状态初始化,一个字母都没有,也是一种方案
for (int i = 0; i < n; ++i) {
// 枚举j的位置,不包括子串的末尾len
for (int j = 0; j < len; ++j) {
for (char k = 'a'; k <= 'z'; ++k) { // i的26种选法,也可视为状态j的26种出边
int u = j;
// str[i]失配跳next,得到最后的kmp位置,在这不论是单个字母还是字符串,kmp都是while,
// 在此是一个字母就只判断一次,最终停下来u大多都是0吧
while (u > 0 && k != str[u]) {
u = next[u];
}
if (k == str[u]) {
u ++;
}
if (u < len) {
dp[i + 1][u] = (dp[i + 1][u] + dp[i][j]) % MOD; // 采用后继更新前驱节点
}
}
}
}
int res = 0;
for (int i = 0; i < len; ++i) {
res = (res + dp[n][i]) % MOD;
}
return res;
}
}
你知道的越多,你不知道的越多。
有道无术,术尚可求,有术无道,止于术。
如有其它问题,欢迎大家留言,我们一起讨论,一起学习,一起进步。