用于查找具有其他字符串的所有字符的子字符串的最小长度的算法

时间:2014-05-30 10:06:22

标签: algorithm

我有两个字符串:
string1 - hello how are you
String2 - olo(包括空格字符)

输出:lo ho(hel lo ho w是你)

lo ho是唯一包含string2所有字符的子字符串。 任何人都可以为此建议一个好的算法(我可以认为只有蛮力算法 - O(n ^ 2)。

同样输出应该是最小长度字符串(如果有多个选项)。

4 个答案:

答案 0 :(得分:6)

l中未出现的字符串2中的字符保留两个指针rM = character -> count以及一个哈希表s[l..r]

最初设置l = 0r,以便string1[l..r]包含string2的所有字符(如果可能)。你可以通过从M中删除字符直到它为空来完成。

然后在每个步骤中将r递增1,然后尽可能地增加l,同时仍保持M为空。所有r - l + 1的最小值(子串s[l..r]的长度)都是解决方案。

Pythonish伪代码:

n = len(string1)
M = {}   # let's say M is empty if it contains no positive values
for c in string2:
    M[c]++
l = 0
r = -1
while r + 1 < n and M not empty:
    r++
    M[string1[r]]--
if M not empty: 
    return "no solution"
answer_l, answer_r = l, r
while True:
    while M[string1[l]] < 0:
        M[string1[l]]++
        l++
    if r - l + 1 < answer_r - anwer_l + 1:
        answer_l, answer_r = l, r
    r++
    if r == n:
        break
    M[string1[r]]--
return s[answer_l..answer_r]

如果在执行递增和递减操作时保持正条目数,则可以在O(1)中实现“为空”检查。

nstring1的长度,mstring2的长度。

请注意,lr只会递增,因此最多有O(n)个增量,因此最后一个O(n)指令在最后一个外部循环中执行。

如果将M实现为数组(我假设字母表是常量大小),则会获得运行时 O(n + m),这是最佳的。如果字母表太大,您可以使用哈希表来获得预期的O(n + m)。

执行示例:

string1 = "abbabcdbcb"
string2 = "cbb"

# after first loop
M = { 'a': 0, 'b': 2, 'c': 1, 'd': 0 }

# after second loop
l = 0
r = 5
M = { 'a': -2, 'b': -1, 'c': 0, 'd': 0 }

# increment l as much as possible:
l = 2
r = 5
M = { 'a': -1, 'b': 0, 'c': 0, 'd': 0 }

# increment r by one and then l as much as possible
l = 2
r = 6
M = { 'a': -1, 'b': 0, 'c': 0, 'd': -1 }

# increment r by one and then l as much as possible
l = 4
r = 7
M = { 'a': 0, 'b': 0, 'c': 0, 'd': -1 }

# increment r by one and then l as much as possible
l = 4
r = 8
M = { 'a': 0, 'b': 0, 'c': -1, 'd': -1 }

# increment r by one and then l as much as possible
l = 7
r = 9
M = { 'a': 0, 'b': 0, 'c': 0, 'd': 0 }

最佳解决方案是s [7..9]。

答案 1 :(得分:0)

我会计算string2string1内的字符位置,然后选择最低和最高字符位置之间距离最小的排列:

#          positions are:
#          01234567890123456
string1 = 'hello how are you'
string2 = 'olo'

# get string1 positions for each character from set(string2)
positions = {'o': [4, 7, 15],
             'l': [2, 3]}

# get all permutations of positions (don't repeat the same element)
# then pick the permutation with minimum distance between min and max position
# (obviously, this part can be optimized, this is just an illustration)
permutations = positions['o'] * positions['l'] * positions['o']
permutations = [[4,2,7], [4,3,7], [4,2,15], ...]
the_permutation = [4,3,7]

# voilà
output = string1_without_spaces[3:7]

答案 2 :(得分:0)

这是使用JavaScript实现的一个示例。逻辑与@Aprillion上面写的类似。

DEMO:http://jsfiddle.net/ZB6vm/4/

var s1 = "hello how are you";
var s2 = "olo";
var left, right;
var min_distance;
var answer = "";

// make permutation recursively
function permutate(ar, arrs, k) {
    // check if the end of recursive call
    if (k == arrs.length) {
        var r = Math.max.apply(Math, ar);
        var l = Math.min.apply(Math, ar);
        var dist = r - l + 1;
        if (dist <= min_distance) {
            min_distance = dist;
            left = l;
            right = r;
        }
        return;
    }
    for (var i in arrs[k]) {
        var v = arrs[k][i];
        if ($.inArray(v, ar) < 0) {
            var ar2 = ar.slice();
            ar2.push(v);
             // recursive call
            permutate(ar2, arrs, k + 1);
        }
    }
}

function solve() {
    var ar = [];   // 1-demension array to store character position
    var arrs = []; // 2-demension array to store character position
    for (var i = 0; i < s2.length; i++) {
        arrs[i] = [];
        var c = s2.charAt(i);
        for (var k = 0; k < s1.length; k++) { // loop by s1
            if (s1.charAt(k) == c) {
                if ($.inArray(k, arrs[i]) < 0) {
                    arrs[i].push(k); // save position found
                }
            }
        }
    }
    // call permutate
    permutate(ar, arrs, 0);
    answer = s1.substring(left, right + 1);
    alert(answer);
}

solve();

希望这有帮助。

答案 3 :(得分:0)

有这种算法在O(N)中执行此操作。

想法:有2个阵列,即。 isRequired [256]和isFound [256]告诉S中每个字符的频率,并在解析字符串S时,查找已找到的每个字符的频率。此外,请保留一个计数器,告知何时找到有效窗口。一旦找到有效窗口,我们就可以移动窗口(向右),保持问题的给定不变量。

C ++程序:

void findMinWindow(const char *text, const char *pattern, int &start, int &end){
        //Calcuate lengths of text and pattern
        int textLen = strlen(text);
        int patternLen = strlen(pattern);

        // Declare 2 arrays which keep tab of required & found frequency of each char in pattern
        int isRequired[256] ; //Assuming the character set is in ASCII
        int isFound[256];
        int count = 0; //For ascertaining whether a valid window is found

        // Keep a tab of minimum window 
        int minimumWindow = INT_MAX;

        //Prepare the isRequired[] array by parsing the pattern
        for(int i=0;i<patternLen;i++){
            isRequired[pattern[i]]++;
        }

        //Let's start parsing the text now
        // Have 2 pointers: i and j - both starting at 0
        int i=0;
        int j=0;
        //Keep moving j forward, keep i fixed till we get a valid window
        for(c=j;c<textLen;c++){
           //Check if the character read appears in pattern or not
           if(isRequired[text[c]] == 0){
              //This character does not appear in the pattern; skip this
              continue;
           }
           //We have this character in the pattern, lets increment isFound for this char
           isFound[text[c]]++;

           //increment the count if this character satisfies the invariant
           if(isFound[text[c]] <= isRequired[text[c]]){
              count++;
           }

           //Did we find a valid window yet?
           if(count == patternLen){
              //A valid window is found..lets see if we can do better from here on
              //better means: increasing i to reduce window length while maintaining invariant
              while(isRequired[s[i]] == 0 || isFound[s[i]] > isRequired[s[i]]){
                   //Either of the above 2 conditions means we should increment i; however we 
                   // must decrease isFound for this char as well.
                   //Hence do a check again
                   if(isFound[s[i]] > isRequired[s[i]]){
                      isFound[s[i]]--;
                   }
                   i++;
              }

               // Note that after the while loop, the invariant is still maintained
               // Lets check if we did better
               int winLength = j-i+1;
               if(winLength < minimumWindow){
                  //update the references we got
                  begin = i;
                  end = j;
                  //Update new minimum window lenght
                  minimumWindow = winLength;
               }
          } //End of if(count == patternLen)
     } //End of for loop 
}