算法 - 字符串 - 面试 求一个字符串的最大不重复子串 - 滑动窗口

这是我面试时的一道算法题,题目是这样的

// 获取字符串的不重复最大字串  比如
abbbbbbcd 返回 bcd
abefbca 返回 efbca
这道题怎么做呢,这道算法题要用滑动窗口来做
我们设定一个窗口 [left, right]  
我们先看最完美的情况,字符串中都不重复
abcde   这种情况 滑动窗口 [left,right]  left =0 字符串第1个下标 ,right 就是一个一个字符往右边走,逐步加1,也就是循环。 所以结果就是
滑动窗口 [0,0]  => a
滑动窗口 [0,1]  => ab
滑动窗口 [0,2]  => abc
滑动窗口 [0,3]  => abcd
滑动窗口 [0,4]  => abcde    
显然,上面5个滑动窗口中最长的结果就是 最后一个 => abcde
那如果存在重复呢,我们来看 
abcbef 这个例子
 滑动窗口 [0,0]  => a
滑动窗口 [0,1]  => ab
滑动窗口 [0,2]  => abc
      滑动窗口 [0,3]  => abcb  => 这里出现了重复,所以这个滑动窗口不行,为了不重复,它只能是cb,那么滑动窗口的左边是2  => [2,3]  = cb
滑动窗口 [2,3] 出  => cb
滑动窗口 [2,4]  => cbe
滑动窗口 [2,5]  => cbef
结果,上面6个滑动窗口中,最长的是cbef
 
发现规律没,也就是说,滑动窗口左边的初始值left =0, 滑动窗口右边的值right 就是一个一个循环下标0,1,2,....   
如果循环过程中,一旦发现哪一个字符在之前出现过,那么要更改什么 ?  =>  更改滑动窗口的左边值left, 把left值更新为 这个字符上一次出现位置k的下一个位置 ,也就是 left = k + 1 形成新的窗口左值,继续后面的循环
上面例子中, 原始滑动窗口[0,3] = abcb 这个时候下标循环到3,字符是b,但发现这个字符之前出现过,上一次出现的位置是1, 所以我们要更新这个滑动窗口的left, left = 1 + 1 = 2, 所以这里窗口应该变成[2,3] = cb
 
所以这个题目的关键在2步 
1. 发现重复字符时,更新滑动窗口的左值left  => 这里我们需要知道这个重复字符上一次出现的位置,我们可以用Dictionary来保存字符串中每种字符最新出现的位置
2.  把每一次滑动窗口的值记下来,取字符个数最多的那个值,就是我们要的结果
 
比如 abca  这里我们用一个Dictionary来保持字符位置
Dictionary<key,value>() {
[a,0], - a出现的位置是0
[b,1], - b 出现的位置是1
[c,1]  - c出现的位置是2
}        当循环到第3个位置,发现是a,它在Dictionary里面已经有了,里面的位置是0,现在位置是3,所以把位置更新成最近的位置3
Dictionary<key,value>() {
[a,3], - a最近出现的位置是3
[b,1], - b 出现的位置是1
[c,1]  - c出现的位置是2
} 
 
好了,代码如下
using System;
using System.Collections.Generic;

public class Solution
{
    /// <summary>
    /// 最长无重复字符子串
    /// </summary>
    public static string LongestUniqueSubstring(string s)
    {
        // key:字符,value:字符最近出现的下标
        Dictionary<char, int> map = new Dictionary<char, int>();
        int left = 0;          // 窗口左边界
        int maxLength = 0;     // 记录最长子串长度
        int startPos = 0;      // 最长子串起始位置

        // right 窗口右边界,不断向右扩张
        for (int right = 0; right < s.Length; right++)
        {
            char c = s[right];
            // 字符在窗口内已经出现过,移动左边界到重复字符下一位
            if (map.ContainsKey(c) && map[c] >= left)
            {
                left = map[c] + 1; //出现重复字符,把窗口左边界left的值更新为重复字符上一次位置+ 1
            }
            // 更新字符最新位置
            map[c] = right;  //更新Dictionary里面该重复字符最近一次的位置

            // 判断当前窗口是否更长,更新最大值
            int currentLen = right - left + 1;
            if (currentLen > maxLength)
            {
                maxLength = currentLen;
                startPos = left;
            }
        }
        // 截取最长无重复子串
        return s.Substring(startPos, maxLength);
    }

    public static void Main()
    {
        string test = "abbbbbbcd";
        string ans = LongestUniqueSubstring(test);
        Console.WriteLine(ans); // 输出 bcd
    }
}

 

 

posted on 2026-09-28 23:29  新西兰程序员  阅读(2)  评论(0)    收藏  举报