不包含给定字符串的子串数(大约束)

时间:2017-12-01 13:07:16

标签: string substring trie suffix-array

我最近在网上发现了一个有趣的问题。以下简要说明:

请注意,总时间限制不应存在1.00s(时间复杂度<10 ^ 8)

现在,学生A找到了一个只包含小写字符的字符串。他想把一个子串切换为学生B作为礼物。学生B有一个他认为是&#34;丑陋&#34;的字符串列表。你可以帮助学生A找到削减子串的方法的数量,这个子串不包含任何&#34;丑陋的&#34;字符串。 (注意相同的子串,但从不同的位置也算)。

Example:
Student A: abcdabcdab
Ugly strings: cd, da

Output: 17

Explanation:
    The 17 cuttings are "a" (appears 3 times), "ab" (appears 3 times), 
    "abc" (appears 2 times), "bc" (appears 2 times), "b" (appears 3 times),
    "c" (appears 2 times) and "d" (appears 2 times)

我首先认为这是一个简单的问题,但这种约束非常大。学生A的字符串的最大长度为100000,而最多可能有500000个丑陋的字符串,最大长度为500000。

我尝试使用后缀trie来解决这个问题,但由于内存限制而失败了。任何人都可以建议一种可能的方法来解决问题。这是一些与高级数据结构相关的问题,例如后缀数组

建议使用任何编程语言的代码,并优先使用适当的描述。因为如果有实际的代码可以研究,我会发现它更好。

1 个答案:

答案 0 :(得分:0)

由于从不同位置开始的相等子串计为不同的子串,因此长度为n的字符串的最大子串数为n *(n + 1)/ 2.(从n位开始的n个子串,n-1个子串从位置1开始,依此类推)。

如果一个丑陋的字符串包含在从位置p开始的长度为q的子字符串中,则从p开始的所有子字符串的长度都为> q也会包含那个丑陋的字符串。

如果丑陋的字符串比子字符串本身长,则它将不匹配。

我的第一次尝试看起来像这样:

String ugly[]; // is provided somehow; at most 500000 with max length of 500000
String student; // the String to cut into substrings, max length 100000
long num = 0;

ugly.sort(); // by length

for (int start = 0; start < student.size() - 1, ++start) {
    for (int end = start + 1; end < student.size(); ++end) {
        String s = student.substr(start, end);
        int lgth = s.size();
        int u = 0;
        while (lgth >= ugly[u].size()) {
            if (s.contains(ugly[u])) break;
            ++u;
        }
        if (lgth < ugly[u].size()) {
            ++num; // we checked all potentially matching uglies
        } else {
            break; // leave the inner loop and 
                   // start with the next position
        }
    }
}

我的第二次尝试将采取另一种方法。如果这项任务必须执行的时间超过几次,我就会开始开发这个。

如果我有一个字符串student和一个长度为p的丑陋字符串匹配某个地方,字符串student可以分成两部分:第一部分以第一个p-1字符结尾丑陋的字符串和第二部分以丑陋的字符串的最后一个p-1字符开头。

这可以重复,直到丑陋的字符串在任何地方都不匹配。然后我们有许多子串和一个与它们中的任何一个都不匹配的丑陋字符串。因此,这个丑陋的字符串可以被丢弃。

对所有丑陋的字符串重复此操作,您将获得一个与任何丑陋字符串都不匹配的“最长”子字符串列表。 现在,您可以遍历此列表并将长度*(长度+ 1)/ 2添加到最终结果。

所以它看起来像

String ugly[]; // as before
String student; // as before
long num = 0;
Vector substrs = new Vector();

ugly.sort(); // by length
substrs.add(student);

void splitStr(String str2split, String pattern, Vector result)
{
    if (str2split.size() < pattern.size()) {
        result.add(str2split);
        return;
    } else {
        int pos = str2split.contains(pattern); // returns position, -1 if not found
        if (pos >= 0) { // found
            String s1 = str2split.substr(0, pos + pattern.size() - 1);
            String s2 = str2split.substr(pos + 1, str2split.size());
            // add s1 and repeat split on s2
            result.add(s1);
            splitStr(s2, pattern, result);
        } else {
            // not found, entire string is ok
            result.add(str2split);
        }
    }
}

for (int u = 0; u < ugly.size(); ++u) {
    Vector newSubstrs = new Vector();
    String ugly2test = ugly[u];
    for (int i = 0; i < substrs.size(); ++i) {
        String t = substrs.get(i);
        splitStr(t, ugly2test, newSubstrs);
    }
    substrs = newSubstrs;
}

for (int i = 0; i < substrs.size(); ++i) {
    String s = substrs.get(i);
    num += s.size() * (s.size() + 1) / 2;
}

注意:这基本上就是这个想法。我没有测试任何代码(这是java,但可能不会编译),我只是在某种伪代码中翻译了我的纯文本想法。