题目描述
给定一个字符串,其中某些字符被一对颜色标签 包裹住。标签可能互相嵌套,每个字符的颜色由包裹住其最内层的标签确定,问红黄蓝颜色的字母各有多少个。
本题需要注意两点:一是字符串可能包含空格,二是可能存在字符没有被任何标签包裹住。
解题思路
由于需要字符串进行分析,首先我们要考虑如何将字符串分割。这里有两个条件:
<和>之间为一个标签,比如<yellow>,</yellow>>和<之间为一段字符串,比如<red>aaaa</red>中的aaaa
根据这两个条件,我们可以将整个字符串分解成一个一个部分,比如:
<yellow>aaa<blue>bbb</blue>ccc</yellow>dddd<red>abc</red>
分解为:
<yellow>,aaa,<blue>,bbb,</blue>,ccc,</yellow>,dddd,<red>,abc,</red>
对于分割的过程,可以参考下面的代码,其中word[]是分割后的数组,n是长度:
prev = 0
n = 0
word = []
For i = 1 .. the length of string
    If string[i] == '<' Then
        // string[prev .. i - 1]的部分为一个字符串
        // 此处有可能出现 prev > i - 1 的情况,则表示出现了连续标签,如"<red><blue>"
        // 若出现这种情况,则不加入word中。
        If prev <= i - 1 Then word[++n] = string[prev .. i - 1]
        prev = i
    Else 
        If string[i] == '>' Then
            //string[prev .. i]的部分为一个标签
            word[++n] = string[prev .. i]
            prev = i + 1
        End If
    End If
End For
由于中标签是可以嵌套定义的,而一段字符串的颜色是跟直接包含它的标签相关。因此对于:
<yellow><blue>word</blue></yellow>
其中word最终显示为蓝色。
对于这种嵌套的处理,这里所采用的做法是将子标签进行递归,并且子标签内所有内容不计入父标签。
举个例子:
<yellow>,aaa,<blue>,word,</blue>,bbb,</yellow>
首先我们读取到<yellow>,我们知道有一个新的标签,新建计数器cnt = 0。
接下来处理了aaa,这是一个字符串,所以直接统计字母个数,cnt = 3。
紧接着我们读取到了<blue>,我们发现这是一个新的标签,因此我们进入递归的过程。直到遇到</blue>,再跳出回到当前。
之后我们读取到了bbb,仍然加入当前<yellow>的计数器,cnt = 3+3 = 6。
最后读取到</yellow>,结束这个<yellow>标签。
将以上的过程写成伪代码为:
// 假设将字符串分割之后得到word[]数组,长度为n
// word[i]表示第i个部分
// color是当前颜色
dfs(i, color)
    cnt = 0
    While (i <= n)
        If (word[i]不是标签)
            cnt = cnt + word[i].length
        Else    // word[i]是标签
            If (word[i]是起始标签)
                i = dfs(i+1, getColor(word[i]))    // 一个子标签,迭代处理,并返回子标签结尾
            Else
                Break         // 结束标签,因此跳出循环
            End If
        End If
        i = i + 1
    End While
    color对应的颜色统计增加cnt
    Return i    // 当前结束标签的位置
由于每个word只会处理一次,因此整个程序时间复杂度为 O(n)。
对于上面的dfs算法,如果仔细观察,可以发现其本质就是一个颜色入栈出栈的过程。
当发现开始标签时,进行入栈;当发现结束标签时进行出栈。
因此伪代码可以写作:
top = 0
For i = 1 .. n
    If (word[i]不是标签)
        If (top != 0) // 考虑可能出现不在标签内的字符串
            stack[top].cnt = stack[top].cnt + word[i].length
        End If
    Else
        If (word[i]是起始标签)
            top = top + 1
            stack[top].color = getColor( word[i] ); // 获取标签的颜色
            stack[top].cnt = 0
        Else
            stack[top].color对应的颜色统计增加stack[top].cnt
            top = top - 1
        End If
    End If
End For
并且可以将整个预处理的过程也放在手工栈中:
prev = 0
For i = 1 .. the length of string
    If string[i] == '<' Then
        //string[prev .. i - 1]的部分为一个字符串
        If (top != 0) // 考虑可能出现不在标签内的字符串
            stack[top].cnt = stack[top].cnt + i - prev
        End If
        prev = i
    Else 
        If string[i] == '>' Then
            // string[prev .. i]的部分为一个标签
            If (string[prev .. i]是起始标签)
                top = top + 1
                stack[top].color = getColor( string[prev .. i]] );  // 获取标签的颜色
                stack[top].cnt = 0
            Else
                stack[top].color对应的颜色统计增加stack[top].cnt
                top = top - 1
            End If
            prev = i + 1
        End If
    End If
End For
这两种方法都可以顺利解决该题目,你可以选择擅长的方法来解决。

有影响 题目分析里这部分没有细讲