算法作业系列10——Unique Substrings in Wraparound String

算法作业系列(十)

Unique Substrings in Wraparound String

写在前面

如果你是因为没有思路来找答案的,这里建议你去看看题目的s,因为s是一个连续字母的字符串,所以,在这里s并不是一个具体的字符串,而是一个限制条件而已,因此,希望没有思考的你再去看看题目,因为动态规划的问题,其实写多了也就是那么回事,想通了就好,其实是有套路的,而且个人认为套路比贪心还要简单。所以,这种套路学会了,一些简单层次的动态规划问题就很简单了。

题目

Consider the string s to be the infinite wraparound string of “abcdefghijklmnopqrstuvwxyz”, so s will look like this: “…zabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcd….”.

Now we have another string p. Your job is to find out how many unique non-empty substrings of p are present in s. In particular, your input is the string p and you need to output the number of different non-empty substrings of p in the string s.

Note: p consists of only lowercase English letters and the size of p might be over 10000.

Example 1:
Input: "a"
Output: 1
Explanation: Only the substring "a" of string "a" is in the string s.
Example 2:
Input: "cac"
Output: 2
Explanation: There are two substrings "a", "c" of string "cac" in the string s.
Example 3:
Input: "zab"
Output: 6
Explanation: There are six substrings "z", "a", "b", "za", "ab", "zab" of string "zab" in the string s.

思路

既然是在动态规划这一类里面,我们就从动态规划的角度去思考问题,其实动态规划也就是把一个大的问题拆分成一个小一点的问题,伴随而来的一般就是一个可以解决的问题,也就是说,动态规划的问题可以将现有问题分成一些小一点的问题和一个逻辑上可以直接解决出来的问题。而最重要的就是把公式推导出来,比如这一题,我以一个数组f代表每个位置会添加多少个新的子串,那就很好知道,如果当前的字符是上一个字符的下一个字母,那么当前的这个字符的加入,就可能会使得整体的子串的数目加上f(n - 1)个,那如果不连续呢,就为0,这里举个例子来看:
zabd是给出的字符串,那么第一个字符为z,肯定没办法带来新的收益(这里带来的收益指的是新增加的子串的数目,长度至少为2);
而对于a,由于z和a是连续的,所以a的加入会带来收益,那具体多少的收益呢?我们看上一个字符z,收益为0,也就意味着z这里跟前面是不连续的,那么a的加入就只会带来za一个收益,我们记录下来为2;
再看b,发现和a是连续的,那么说明它也有可能带来收益,那带来多少呢?我们看前一个字母a,由于这里的a带来的收益为1,那就说明a这里最长的连续子串长度为2,那对于b,带来的收益就有ab、zab这两个;
最后看d,发现不连续,既然我们考虑的只有长度为2及以上的字符串,那么我们就记为0,因为没法带来收益。

经过上面的流程,我觉得大家应该很容易把状态转移方程写出来了:
f(i) = f(i - 1) + 1(连续) f(i) = 0(不连续),第一个字母的值为0最。后的结果就是出现的字母的数目加上每一个f里面的值的和。

那我们接下来解释一下为什么说可能带来收益,我们看这么一个字符串abdab,我们看这里的b,就会有一个情况,就是前面的b为1,后面的b也为1,但是这里都是ab的字符串,所以就重复了,那怎么解决呢?答案就是,对于每一个字母,只去带来最多收益的那个。

好,那我们只需要每次记录下当前字母的值,再和以前的最大值比较,看看谁更大就要谁就好了。

参考代码

class Solution {
public:
    int findSubstringInWraproundString(string p) {
        if (p.length() == 0) {
            return 0;
        }
        int* num = new int[p.length()];
        map<char, int> cnt;
        num[0] = 0;
        cnt[p[0]] = 0;
        for (int i = 1; i < p.length(); i++) {
            cnt[p[i]] = cnt[p[i]];
            if (p[i] == p[i - 1] + 1 || (p[i - 1] == 'z' && p[i] == 'a')) {
                num[i] = num[i -1] + 1;
                if (cnt[p[i]] < num[i]) {
                    cnt[p[i]] = num[i];
                }
            } else {
                num[i] = 0;
            }
        }

        int total = cnt.size();
        for (map<char, int>::iterator i = cnt.begin(); i != cnt.end(); i++) {
            total += i->second;
        }
        return total;
    }
};
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章