
1) 【一句话结论】:在语音识别任务中,通过动态规划构建二维DP表记录子问题最优解,最终得到最长公共子序列(LCS),时间复杂度O(mn),空间复杂度可通过优化降为O(min(m,n))。
2) 【原理/概念讲解】:
首先明确LCS问题:给定两个序列X(长度m)和Y(长度n),找到它们的最长公共子序列(子序列不要求连续,但顺序保持)。动态规划的核心是“最优子结构”——原问题解可由子问题解推导。
定义状态:设dp[i][j]表示X前i个字符与Y前j个字符的LCS长度(i、j从1开始,dp[0][]=dp[][0]=0表示空序列与任何序列的LCS为0)。
状态转移方程:
3) 【对比与适用场景】:
| 对比项 | 最长公共子序列(LCS) | 最长公共子串(Longest Common Substring) |
|---|---|---|
| 定义 | 子序列(顺序保持,不要求连续) | 子串(连续字符序列) |
| 转移方程 | 匹配则+1,否则取上/左最大值 | 匹配则+1,否则0 |
| 适用场景 | 语音片段相似性分析、文本对齐、生物序列比对 | 语音识别中连续短语匹配、字符串匹配 |
| 注意点 | 计算量较大,需空间优化 | 连续性要求,计算量相对较小 |
4) 【示例】(伪代码):
def lcs(X, Y):
m, n = len(X), len(Y)
dp = [[0] * (n+1) for _ in range(m+1)]
for i in range(1, m+1):
for j in range(1, n+1):
if X[i-1] == Y[j-1]:
dp[i][j] = dp[i-1][j-1] + 1
else:
dp[i][j] = max(dp[i-1][j], dp[i][j-1])
# 回溯得到序列
lcs_seq = []
i, j = m, n
while i > 0 and j > 0:
if X[i-1] == Y[j-1]:
lcs_seq.append(X[i-1])
i -= 1
j -= 1
elif dp[i-1][j] > dp[i][j-1]:
i -= 1
else:
j -= 1
return dp[m][n], lcs_seq[::-1]
# 示例:X="ABCBDAB",Y="BDCABA"
print(lcs("ABCBDAB", "BDCABA")) # 输出 (4, ['B', 'D', 'A', 'B'])
5) 【面试口播版答案】:
“面试官您好,在语音识别任务中解决最长公共子序列(LCS)问题时,我们通常用动态规划。首先,定义状态:设两个序列为X和Y,dp[i][j]表示X前i个字符与Y前j个字符的LCS长度。状态转移方程是:如果X[i-1]等于Y[j-1],那么dp[i][j]等于dp[i-1][j-1]加1;否则取dp[i-1][j]和dp[i][j-1]的最大值。这样填充整个二维表后,dp[m][n]就是LCS的长度。回溯的话,从右下角开始,若字符匹配,就加入序列并往左上角走,否则往最大值的方向走。时间复杂度是O(mn),因为每个状态计算一次;空间复杂度是O(mn),但可以优化为O(min(m,n)),比如只保留一维数组。优化方法就是空间压缩,因为当前状态只依赖上一行或上一列。比如,用一维数组dp[j]表示当前行,上一行是prev[j],计算时更新prev[j]为当前dp[j],这样空间从O(mn)降到O(min(m,n))。这样在语音识别中,当处理长语音片段时,能减少内存占用。总结来说,动态规划通过分解子问题,利用最优子结构,高效解决了LCS问题,优化后适合处理大规模语音数据。”
6) 【追问清单】:
7) 【常见坑/雷区】: