# 表格行匹配算法可视化图示 ## 📐 数据结构可视化 ### 输入数据 ``` HTML表格行(7行): ┌─────────────────────────────────────────────────────────┐ │ 行0: 部门Department: 01372403999 柜员Search Teller... │ ├─────────────────────────────────────────────────────────┤ │ 行1: 账号/卡号Account/Card No: 6222621010026732125... │ ├─────────────────────────────────────────────────────────┤ │ 行2: 查询起日Query Starting Date: 2023-08-12... │ ├─────────────────────────────────────────────────────────┤ │ 行3: 查询时间Query Time: 2024年08月12日 11:21:23... │ ├─────────────────────────────────────────────────────────┤ │ 行4: 证件种类 ID Type: 第二代居民身份证... │ ├─────────────────────────────────────────────────────────┤ │ 行5: Serial Num | Trans Date | Trans Time | ... (表头) │ ├─────────────────────────────────────────────────────────┤ │ 行6: 1 | 2023-08-12 | 13:04:52 | 网上银行卡转入... │ └─────────────────────────────────────────────────────────┘ OCR文本分组(48组): ┌─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┐ │ 组0 │ 组1 │ 组2 │ 组3 │ 组4 │ 组5 │ ... │ 组46│ 组47│ ├─────┼─────┼─────┼─────┼─────┼─────┼─────┼─────┼─────┤ │部门 │账号 │查询 │查询 │证件 │序号 │ ... │司 │9646 │ │信息 │信息 │起日 │时间 │信息 │表头 │ │2023 │42s │ └─────┴─────┴─────┴─────┴─────┴─────┴─────┴─────┴─────┘ ``` --- ## 🔄 DP状态转移可视化 ### 阶段1:初始化第一行(i=0) ``` HTML行0的匹配搜索空间: ┌─────────────────────────────────────────────────────────────┐ │ OCR组索引: 0 1 2 3 4 5 ... 18 │ │ ┌────┐ │ │ │组0 │ ← 单独匹配 │ │ └────┘ │ │ ┌────┬────┐ │ │ │组0 │组1 │ ← 合并2组 │ │ └────┴────┘ │ │ ┌────┬────┬────┐ │ │ │组0 │组1 │组2 │ ← 合并3组 (最优: 得分0.98) │ │ └────┴────┴────┘ │ │ ┌────┬────┬────┬────┐ │ │ │组0 │组1 │组2 │组3 │ ← 合并4组 │ │ └────┴────┴────┴────┘ │ └─────────────────────────────────────────────────────────────┘ DP状态矩阵(初始化后): OCR组索引 j → HTML 0 1 2 3 4 5 ... 47 行↓ 0 [0.98] - - - - - ... - ↑ 最优匹配: HTML行0 → OCR组[0,1,2] ``` --- ### 阶段2:处理第二行(i=1) ``` 从 dp[0][2] = 0.98 出发,搜索HTML行1的匹配: 搜索窗口(SEARCH_WINDOW=15): ┌─────────────────────────────────────────────────────────────┐ │ 上一行结束位置: 组2 │ │ │ │ 搜索范围: 组3 到 组17 (最多向前15个组) │ │ │ │ 组索引: 2 [3] [4] [5] [6] ... [17] │ │ ↑ │ │ │ │ │ │ │ 起点 └────┴────┴────┴─────────┘ │ │ 搜索窗口 │ └─────────────────────────────────────────────────────────────┘ 匹配尝试: ┌─────────────────────────────────────────────────────────────┐ │ gap=0, start_j=3, count=1 → end_j=3 │ │ 匹配: 组3单独 │ │ 相似度: 0.85, 得分: 0.98 + 0.85 = 1.83 │ │ │ │ gap=0, start_j=3, count=2 → end_j=4 │ │ 匹配: 组3+组4 │ │ 相似度: 0.92, 得分: 0.98 + 0.92 = 1.90 ✅ (最优) │ │ │ │ gap=1, start_j=4, count=1 → end_j=4 │ │ 匹配: 组4单独 │ │ 相似度: 0.20, 得分: 0.98 + 0.20 = 1.18 │ └─────────────────────────────────────────────────────────────┘ DP状态矩阵更新: OCR组索引 j → HTML 0 1 2 3 4 5 ... 47 行↓ 0 [0.98] - - - - - ... - 1 - - - 1.83 [1.90] - ... - ↑ ↑ 候选1 最优匹配 ``` --- ### 阶段3:完整DP过程(简化版) ``` DP状态矩阵(完整过程): OCR组索引 j → HTML 0 1 2 3 4 5 6 7 ... 40 41 ... 47 行↓ 0 [0.98] - - - - - - - ... - - ... - │ └─→ 匹配组[0,1,2] 1 - - - 1.83 [1.90] - - - ... - - ... - │ │ └─────┴─→ 匹配组[3,4] 2 - - - - - [2.85] - - ... - - ... - │ └─→ 匹配组[5,6] 3 - - - - - - [3.80] - ... - - ... - │ └─→ 匹配组[7,8,9] 4 - - - - - - - [4.75] ... - - ... - │ └─→ 匹配组[10,11] 5 - - - - - - - - ... [5.20] - ... - │ └─→ 匹配组[12..39] 6 - - - - - - - - ... - - ... [5.50] │ └─→ 匹配组[40..47] ✅ ``` --- ## 🔍 回溯路径可视化 ### 从最优终点回溯 ``` 最优终点: dp[6][47] = 5.50 回溯过程: ┌─────────────────────────────────────────────────────────────┐ │ 步骤1: (i=6, j=47) │ │ path[(6, 47)] = (prev_j=45, start_j=46) │ │ → HTML行6匹配OCR组[46, 47] │ │ → 移动到 (i=5, j=45) │ │ │ │ 步骤2: (i=5, j=45) │ │ path[(5, 45)] = (prev_j=39, start_j=40) │ │ → HTML行5匹配OCR组[40..45] │ │ → 移动到 (i=4, j=39) │ │ │ │ 步骤3: (i=4, j=39) │ │ path[(4, 39)] = (prev_j=37, start_j=38) │ │ → HTML行4匹配OCR组[38, 39] │ │ → 移动到 (i=3, j=37) │ │ │ │ ... 继续回溯 ... │ │ │ │ 步骤7: (i=0, j=2) │ │ path[(0, 2)] = (prev_j=-1, start_j=0) │ │ → HTML行0匹配OCR组[0, 1, 2] │ │ → 回溯结束 │ └─────────────────────────────────────────────────────────────┘ 最终匹配结果: HTML行0 ──────→ OCR组[0, 1, 2] HTML行1 ──────→ OCR组[3, 4] HTML行2 ──────→ OCR组[5, 6] HTML行3 ──────→ OCR组[7, 8, 9] HTML行4 ──────→ OCR组[10, 11] HTML行5 ──────→ OCR组[12, 13, ..., 39] HTML行6 ──────→ OCR组[40, 41, ..., 47] ``` --- ## 🎯 关键概念图示 ### 1. 合并文本缓存(merged_cache) ``` merged_cache[(start_j, count)] = 从start_j开始合并count个组的文本 示例: start_j=0, count=3 ┌─────┬─────┬─────┐ │ 组0 │ 组1 │ 组2 │ → 合并文本: "部门department:...账号/卡号...查询起日..." └─────┴─────┴─────┘ ↑ ↑ ↑ └─────┴─────┘ count=3 ``` ### 2. Gap搜索机制 ``` 上一行结束位置: prev_j = 2 搜索窗口: ┌─────────────────────────────────────────────────────────────┐ │ prev_j=2 │ │ │ │ │ │ gap=0: start_j=3 (紧接上一行) │ │ │ gap=1: start_j=4 (跳过1个组) │ │ │ gap=2: start_j=5 (跳过2个组) │ │ │ ... │ │ │ gap=14: start_j=16 (最多跳过14个组) │ │ │ │ │ └──────────────────────────────────────────────────────────┘ │ SEARCH_WINDOW = 15 └─────────────────────────────────────────────────────────────┘ ``` ### 3. 跳过HTML行的处理 ``` 场景: HTML行i无法匹配任何OCR组 处理方式: ┌─────────────────────────────────────────────────────────────┐ │ HTML行i-1 → OCR组j (得分: 2.5) │ │ │ │ │ ├─→ HTML行i匹配OCR组j+1 (正常匹配) │ │ │ │ │ └─→ HTML行i跳过 (继承状态,得分: 2.5 - 0.3 = 2.2) │ │ → HTML行i+1可以从OCR组j开始匹配 │ └─────────────────────────────────────────────────────────────┘ ``` --- ## 📊 性能优化可视化 ### 剪枝优化 ``` 剪枝前: 上一行有50个有效状态 ┌─────────────────────────────────────────────────────────────┐ │ valid_prev_indices = [0, 1, 2, ..., 49] (50个状态) │ │ │ │ 需要计算: 50 × 15 × 4 = 3000 次匹配 │ └─────────────────────────────────────────────────────────────┘ 剪枝后: 只保留得分最高的30个 ┌─────────────────────────────────────────────────────────────┐ │ valid_prev_indices = [2, 5, 8, ..., 47] (30个状态) │ │ │ │ 需要计算: 30 × 15 × 4 = 1800 次匹配 (减少40%) │ └─────────────────────────────────────────────────────────────┘ ``` ### 长度预筛选 ``` HTML文本长度: 100字符 OCR文本长度: 10字符 长度比例: 10 / 100 = 0.1 < 0.2 (阈值) ┌─────────────────────────────────────────────────────────────┐ │ ❌ 直接跳过,不计算相似度 │ │ 节省计算时间 │ └─────────────────────────────────────────────────────────────┘ ``` --- ## 🔄 完整流程图 ``` 开始 │ ├─→ 提取HTML行文本 (7行) │ ├─→ 提取OCR组文本 (48组) │ ├─→ 检查行数是否相等? │ ├─→ 是 → 验证对角线匹配 │ │ ├─→ 匹配良好 → 1:1映射 → 结束 │ │ └─→ 匹配不佳 → 继续DP │ └─→ 否 → 继续DP │ ├─→ 预计算合并文本缓存 (merged_cache) │ ├─→ 初始化第一行 (i=0) │ └─→ 搜索前15+4个组,找到最佳匹配 │ ├─→ DP状态转移 (i=1..6) │ ├─→ 获取上一行有效状态 │ ├─→ 剪枝(保留前30个) │ ├─→ 允许跳过HTML行 │ └─→ 正常匹配(搜索窗口15,最多合并4组) │ ├─→ 回溯找最优路径 │ └─→ 从最优终点回溯,构建映射关系 │ ├─→ 后处理 │ ├─→ 处理未匹配的OCR组(Orphans) │ └─→ 按Y坐标排序 │ └─→ 返回映射结果 ``` --- ## 💡 实际案例数据流 ### 用户提供的实际数据 ``` HTML行数: 7 OCR组数: 48 HTML行0文本: '部门department:01372403999柜员searchteller:ebf0000...' OCR组0文本: '部门department:01372403999柜员searchteller:ebf0000...' → 相似度: 0.95+ ✅ HTML行5文本: 'serialnum序号transdate交易日期transtime交易时间...' OCR组5-39文本: 'serialtransdatetranstimetradingtypedcflg...' → 相似度: 0.85+ ✅ HTML行6文本: '12023-08-1213:04:52网上银行卡转入贷cr33.52...' OCR组40-47文本: '2023-08-1213:04:5233.52554.5210*****69...' → 相似度: 0.80+ ✅ ``` 这个算法通过动态规划,成功地将7个HTML行与48个OCR组进行了智能匹配!