这是曾经的一个面试题,正好引出状态机编程思想。挺不错的一个例子。
给定一个字符串,它由以下字符组成:
该字符串组成有以下规则限定:
请解决问题:
我们拿到这个问题时,第一感觉往往是顺序遍历字符串,并检测左右相邻字符是否满足边界条件,从而进行分支处理。但是这样做有以下棘手之处:
嗯,不信的话,可以自己按照上述最简单的思路实现一下,你就明白了。
有人说,复杂逻辑我不怕啊,细心就好。So...是时候请出我们的大侠--“状态机”了。
状态机是编译原理中的一种技术,学过电学的读者应该也在《数字电子技术》中用过它,归根结底,就是把复杂的问题逻辑化为一个一个的状态,我们处理问题的过程就是在各个状态之间不断迁移(包含自迁移),这样画出来的图就叫做状态迁移图,帮助我们把一锅难缠的粥转化为一张清晰的网。当然,这里不会深究状态机的概念,详情请自查(比如还有状态迁移表等等)。
让我们用状态迁移图表示上面的问题(若看不清图,可以右键在新的标签页看,或者下载下来看):
我设置了两个状态,一个用来区分括号内外,一个用来区分是否是字母,从而进行不同的处理。
括号内外分成了两个子状态,这两个子状态是互斥的,因此他们内部的状态变量可以共用。
至于状态之间转移条件,直接看代码即可理解:
1 public class CountWords {
2
3 final static int InBracket = 0;// 括号内
4 final static int OutBracket = 1;// 括号外
5
6 final static int IsLetter = 0;// 是字母
7 final static int NotLetter = 1;// 不是字母
8
9 public static void main(String[] args) {
10 test("_yy_()()_(_apple_welcome)_ssjjjs_");//2,6
11 test("__()()_(_)__()_");//0,0
12 test("_ya_");//0,2
13 test("_yy_(_)(r)_(_wel_c_ome_k)_");//5,2
14 test("_yy_aa_");//0,2
15 test("_yy_(aaa_bb_c)()__yyyyy_");//3,5
16 test("(u)_()_(__)()_yy_()");//1,2
17 test("__(a_wwwww)");//2,0
18 test("__(_a_wwwww_)_____ddd____()()()()()()");//2,3
19 }
20
21 public static void test(String str) {
22 // 状态初始化
23 int state_INOUT = OutBracket;
24 int state_letter = NotLetter;
25 // 统计结果初始化
26 int outLengthOfLongestWord = 0;
27 int outLengthOfCurrentWord = 0;
28 int inNumsOfWord = 0;
29 // 开始处理
30 for (int i = 0; i < str.length(); ++i) {
31 // 取出当前字符
32 char c = str.charAt(i);
33 // 根据括号设置状态:括号内、括号外
34 if (c == '(') {
35 state_INOUT = InBracket;
36 }
37 if (c == ')') {
38 state_INOUT = OutBracket;
39 }
40 // 括号内状态
41 if (state_INOUT == InBracket) {
42 if (state_letter == IsLetter) {
43 if (c == '_' || c == ')') {
44 state_letter = NotLetter;
45 }
46 } else if (state_letter == NotLetter) {
47 if (Character.isLetter(c)) {
48 state_letter = IsLetter;
49 ++inNumsOfWord;
50 }
51 }
52 }
53 // 括号外状态
54 else if (state_INOUT == OutBracket) {
55 if (state_letter == IsLetter) {
56 // System.out.println(c);
57 if (c == '_' || c == '(') {
58 if (outLengthOfLongestWord < outLengthOfCurrentWord) {
59 outLengthOfLongestWord = outLengthOfCurrentWord;
60 }
61 outLengthOfCurrentWord = 0;
62 state_letter = NotLetter;
63 } else if (Character.isLetter(c)) {
64 ++outLengthOfCurrentWord;
65 }
66 }
67 if (state_letter == NotLetter) {
68 if (Character.isLetter(c)) {
69 state_letter = IsLetter;
70 ++outLengthOfCurrentWord;
71 }
72 }
73 }
74 }
75 System.out.println("括号内的字符串数:" + inNumsOfWord);
76 System.out.println("括号外的最长字符串长度:" + outLengthOfLongestWord);
77 System.out.println();
78
79 }
80
81 }
有没有感觉到很方便?思路更清晰了,效率也上去了。
注:状态机不同于设计模式中常说的状态模式(状态模式用类代表状态)。
就这么多吧,欢迎提出测试样例找bug,共同进步。