DeepSeek 大规模宕机:高可用架构之痛与腾讯的成本博弈
昨日,DeepSeek 遭遇严重服务中断,持续时间从上午 8 点至下午 1 点,长达近 5 小时。这并非孤立事件,而是今年以来第 18 次大面积异常,也是本周内的第二次(上一次为 10 月 12 日)。
随着办公类 AI Agent 的普及,DeepSeek 的稳定性已不仅影响开发者,更波及庞大的普通用户群体。宕机期间,腾讯旗下产品 WorkBuddy 出现严重排队现象,显示等待人数超 2 万。
为何 WorkBuddy 受 DeepSeek 宕机影响?
核心原因在于成本控制与架构依赖。DeepSeek API 凭借极具竞争力的低价,已成为众多 AI 产品的底层基建。腾讯 WorkBuddy 并未完全自部署模型集群,而是将大部分用户流量通过直连方式导向 DeepSeek API。
这种策略的优势在于:
- 降低算力成本:避免根据用户规模等比配置昂贵的模型集群。
- 资源优化:将珍贵算力集中于自家模型训练。
- 商业闭环:通过积分制度包装 Token 价格,确保边际效应递减,实现低成本扩张。
然而,弊端同样明显:一旦外部依赖服务下线,自有集群无法承接突发流量,导致用户体验受损。尽管面临舆论压力,但在当前办公 Agent 市场争夺期,腾讯大概率仍会坚持“总成本领先”策略,而非立即大幅增加自有集群投入。
LeetCode 691. 贴纸拼词
题目描述
平台:LeetCode | 题号:691
给定一组贴纸 stickers 和一个目标字符串 target。每个贴纸上包含一个小写英文单词。你可以无限次使用每个贴纸,通过切割并重新排列字母来拼出 target。
返回拼出 target 所需的最小贴纸数量。如果不可能完成,返回 -1。
示例 1:
输入:stickers = ["with","example","science"], target = "thehat"
输出:3
解释:使用 2 个 "with" 和 1 个 "example" 可拼出 "thehat"。
示例 2:
输入:stickers = ["notice","possible"], target = "basicbasic"
输出:-1
解释:无法通过给定贴纸拼出目标字符串。
解法一:动态规划(记忆化搜索)
使用状态压缩动态规划。定义 state 为一个整数,其二进制位表示 target 中对应位置的字符是否已被拼出。若第 i 位为 1,表示 target[i] 已满足。
初始状态 state = 0,目标状态 state = (1 << n) - 1。由于每个贴纸可重复使用,对于特定状态,其到达最终状态的最小步数是固定的,适合使用记忆化搜索避免重复计算。
在搜索过程中,枚举每个贴纸,尝试更新当前状态 state 到新状态 nstate。取所有可能新状态中 dfs(nstate) + 1 的最小值。
Java 代码实现:
class Solution {
int N = 20, M = 1 << 20, INF = 50;
int[] f = new int[M];
String[] ss;
String t;
int dfs(int state) {
int n = t.length();
if (state == ((1 << n) - 1)) return 0;
if (f[state] != -1) return f[state];
int ans = INF;
for (String s : ss) {
int nstate = state;
out:
for (char c : s.toCharArray()) {
for (int i = 0; i < n; i++) {
if (t.charAt(i) == c && ((nstate >> i) & 1) == 0) {
nstate |= (1 << i);
continue out;
}
}
}
if (nstate != state) ans = Math.min(ans, dfs(nstate) + 1);
}
return f[state] = ans;
}
public int minStickers(String[] stickers, String target) {
ss = stickers;
t = target;
Arrays.fill(f, -1);
int ans = dfs(0);
return ans == INF ? -1 : ans;
}
}
- 时间复杂度:O(2^n * m * L),其中 n 为 target 长度,m 为 stickers 数量,L 为贴纸平均长度。
- 空间复杂度:O(2^n)
解法二:动态规划(递推)
定义 f[state] 为达到状态 state 所需的最小贴纸数。初始化 f[0] = 0,其余为无穷大。
遍历所有有效状态 s,尝试使用任意贴纸更新得到新状态 ns,执行状态转移:f[ns] = min(f[ns], f[s] + 1)。
Java 代码实现:
class Solution {
static int N = 15, INF = 20;
static int[] f = new int[1 << N];
public int minStickers(String[] ss, String t) {
int n = ss.length, m = t.length(), mask = 1 << m;
Arrays.fill(f, INF);
f[0] = 0;
for (int s = 0; s < mask; s++) {
if (f[s] == INF) continue;
for (String str : ss) {
int ns = s, len = str.length();
for (int i = 0; i < len; i++) {
int c = str.charAt(i) - 'a';
for (int j = 0; j < m; j++) {
if (t.charAt(j) - 'a' == c && (((ns >> j) & 1) == 0)) {
ns |= (1 << j);
break;
}
}
}
f[ns] = Math.min(f[ns], f[s] + 1);
}
}
return f[mask - 1] == INF ? -1 : f[mask - 1];
}
}
- 时间复杂度:O(2^n * m * L)
- 空间复杂度:O(2^n)
解法三:预处理优化
在状态转移时,无需枚举所有贴纸。可以预处理每个贴纸包含的字符集合。当需要填补 target 中某个未满足的字符时,只枚举包含该字符的有效贴纸,从而减少无效计算。
Java 代码实现:
class Solution {
static int N = 15, INF = 20;
static int[] f = new int[1 << N];
public int minStickers(String[] ss, String t) {
int n = ss.length, m = t.length(), mask = 1 << m;
Map
> map = new HashMap<>();
// 预处理:记录哪些贴纸包含特定字符
for (int i = 0; i < n; i++) {
String str = ss[i];
for (char c : str.toCharArray()) {
int d = c - 'a';
List
list = map.getOrDefault(d, new ArrayList<>());
if (list.size() == 0 || list.get(list.size() - 1) != i) list.add(i);
map.put(d, list);
}
}
Arrays.fill(f, INF);
f[0] = 0;
for (int s = 0; s < mask; s++) {
if (f[s] == INF) continue;
// 找到第一个未满足的字符位置
int loc = -1;
for (int i = 0; i < m && loc == -1; i++) {
if (((s >> i) & 1) == 0) loc = i;
}
if (loc == -1) continue;
// 只枚举包含该字符的贴纸
List
list = map.getOrDefault(t.charAt(loc) - 'a', new ArrayList<>());
for (int idx : list) {
String str = ss[idx];
int ns = s, len = str.length();
for (int j = 0; j < len; j++) {
char c = str.charAt(j);
for (int k = 0; k < m; k++) {
if (t.charAt(k) == c && (((ns >> k) & 1) == 0)) {
ns |= (1 << k);
break;
}
}
}
f[ns] = Math.min(f[ns], f[s] + 1);
}
}
return f[mask - 1] == INF ? -1 : f[mask - 1];
}
}
- 时间复杂度:优于朴素 DP,具体取决于字符分布。
- 空间复杂度:O(2^n + m * L)

