解题思路

这是一个滑动窗口问题,需要:

  1. 找到包含所有五种宝石 (ABCDE) 的最短连续子串
  2. 考虑项链首尾相接的特性
  3. 剩余的宝石数量就是答案

解题步骤:

  1. 将字符串复制一遍处理首尾相接的情况
  2. 使用滑动窗口找到包含 ABCDE 的最短子串
  3. 如果找不到包含所有宝石的子串,返回 0
  4. 答案为原字符串长度减去最短子串长度

代码

#include <iostream>
#include <string>
#include <unordered_map>
using namespace std;

int solve(string s) {
    int n = s.length();
    // 处理首尾相接
    s = s + s;
    
    // 记录需要的宝石
    unordered_map<char, int> need = {
        {'A', 1}, {'B', 1}, {'C', 1}, {'D', 1}, {'E', 1}
    };
    int needCount = 5;  // 需要的不同宝石数
    
    // 滑动窗口
    unordered_map<char, int> window;
    int left = 0, right = 0;
    int valid = 0;  // 已找到的不同宝石数
    int minLen = s.length() + 1;  // 最短子串长度
    
    while(right < s.length()) {
        char c = s[right];
        right++;
        
        // 更新窗口数据
        if(need.count(c)) {
            window[c]++;
            if(window[c] == 1) valid++;
        }
        
        // 尝试缩小窗口
        while(valid == needCount) {
            minLen = min(minLen, right - left);
            
            char d = s[left];
            left++;
            
            if(need.count(d)) {
                window[d]--;
                if(window[d] == 0) valid--;
            }
        }
    }
    
    // 如果找不到包含所有宝石的子串
    if(minLen > n) return 0;
    
    // 返回剩余宝石数量
    return n - minLen;
}

int main() {
    string s;
    while(cin >> s) {
        cout << solve(s) << endl;
    }
    return 0;
}
import java.util.*;

public class Main {
    public static int solve(String s) {
        int n = s.length();
        // 处理首尾相接
        s = s + s;
        
        // 记录需要的宝石
        Map<Character, Integer> need = new HashMap<>();
        for(char c : "ABCDE".toCharArray()) {
            need.put(c, 1);
        }
        int needCount = 5;
        
        // 滑动窗口
        Map<Character, Integer> window = new HashMap<>();
        int left = 0, right = 0;
        int valid = 0;
        int minLen = s.length() + 1;
        
        while(right < s.length()) {
            char c = s.charAt(right);
            right++;
            
            // 更新窗口数据
            if(need.containsKey(c)) {
                window.put(c, window.getOrDefault(c, 0) + 1);
                if(window.get(c) == 1) valid++;
            }
            
            // 尝试缩小窗口
            while(valid == needCount) {
                minLen = Math.min(minLen, right - left);
                
                char d = s.charAt(left);
                left++;
                
                if(need.containsKey(d)) {
                    window.put(d, window.get(d) - 1);
                    if(window.get(d) == 0) valid--;
                }
            }
        }
        
        // 如果找不到包含所有宝石的子串
        if(minLen > n) return 0;
        
        // 返回剩余宝石数量
        return n - minLen;
    }
    
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        while(sc.hasNext()) {
            String s = sc.next();
            System.out.println(solve(s));
        }
    }
}
def solve(s):
    n = len(s)
    # 处理首尾相接
    s = s + s
    
    # 记录需要的宝石
    need = {'A': 1, 'B': 1, 'C': 1, 'D': 1, 'E': 1}
    need_count = 5
    
    # 滑动窗口
    window = {}
    left = right = 0
    valid = 0
    min_len = len(s) + 1
    
    while right < len(s):
        c = s[right]
        right += 1
        
        # 更新窗口数据
        if c in need:
            window[c] = window.get(c, 0) + 1
            if window[c] == 1:
                valid += 1
        
        # 尝试缩小窗口
        while valid == need_count:
            min_len = min(min_len, right - left)
            
            d = s[left]
            left += 1
            
            if d in need:
                window[d] -= 1
                if window[d] == 0:
                    valid -= 1
    
    # 如果找不到包含所有宝石的子串
    if min_len > n:
        return 0
    
    # 返回剩余宝石数量
    return n - min_len

if __name__ == "__main__":
    while True:
        try:
            s = input()
            print(solve(s))
        except:
            break

算法及复杂度

  • 算法:滑动窗口
  • 时间复杂度:,其中 为字符串长度
  • 空间复杂度:,哈希表大小固定