需實作類似 Google 進階搜尋「包含所有關鍵字,順序不拘」的查詢。輸入關鍵字集合 {k1, k2, ..., kn},產生 SQL:
SELECT ... FROM table
WHERE (col LIKE '%k1%k2%...%kn%')
OR (col LIKE '%k1%kn%...%k2%')
OR ... -- 所有 n! 種排列
ORDER BY ...;
核心子問題:給定字串陣列,輸出所有全排列組合字串(以 % 分隔)。
採用 Donald Knuth / Rosen 教科書經典演算法:
a[j] < a[j+1] 的索引 ja[k] > a[j] 的索引 ka[j] ↔ a[k]j+1 到末尾的子陣列時間複雜度 O(n! * n),空間 O(n),適合 n ≤ 7 (5040 種) 場景。
import java.math.BigInteger;
import java.util.*;
public class PermutationGenerator {
private final int[] a; // 當前排列索引
private BigInteger numLeft; // 剩餘排列數
private final BigInteger total;
public PermutationGenerator(int n) {
if (n < 1) throw new IllegalArgumentException("Min 1");
a = new int[n];
total = factorial(n);
reset();
}
public void reset() {
for (int i = 0; i < a.length; i++) a[i] = i;
numLeft = new BigInteger(total.toString());
}
public BigInteger getNumLeft() { return numLeft; }
public BigInteger getTotal() { return total; }
public boolean hasMore() { return numLeft.compareTo(BigInteger.ZERO) == 1; }
private static BigInteger factorial(int n) {
BigInteger f = BigInteger.ONE;
for (int i = n; i > 1; i--) f = f.multiply(BigInteger.valueOf(i));
return f;
}
/** 回傳下一個排列的索引陣列 */
public int[] getNext() {
if (numLeft.equals(total)) { numLeft = numLeft.subtract(BigInteger.ONE); return a; }
int j = a.length - 2;
while (j >= 0 && a[j] > a[j + 1]) j--;
int k = a.length - 1;
while (a[j] > a[k]) k--;
swap(j, k);
reverse(j + 1, a.length - 1);
numLeft = numLeft.subtract(BigInteger.ONE);
return a;
}
private void swap(int i, int j) { int t = a[i]; a[i] = a[j]; a[j] = t; }
private void reverse(int l, int r) { while (l < r) swap(l++, r--); }
// 測試主程式
public static void main(String[] args) {
String[] kw = {"tom", "tina"};
PermutationGenerator pg = new PermutationGenerator(kw.length);
StringBuilder sql = new StringBuilder("SELECT * FROM student WHERE ");
boolean first = true;
while (pg.hasMore()) {
int[] idx = pg.getNext();
if (!first) sql.append(" OR ");
first = false;
sql.append("name LIKE '%");
for (int i : idx) sql.append(kw[i]).append('%');
sql.append("'");
}
sql.append(" ORDER BY age");
System.out.println(sql);
}
}
輸入 {"tom", "tina"}:
SELECT * FROM student WHERE name LIKE '%tom%tina%' OR name LIKE '%tina%tom%' ORDER BY age
| 關鍵字數 n | 排列數 n! | SQL 長度估算 | 建議 |
|---|---|---|---|
| 3 | 6 | ~200 字元 | 可直接使用 |
| 5 | 120 | ~3 KB | 可接受,注意 SQL 長度限制 |
| 7 | 5,040 | ~150 KB | 接近資料庫 SQL 長度上限,謹慎 |
| 10 | 3,628,800 | ~100 MB | 不可行,需改用全文搜尋引擎 |