同構——用數論指紋尋找子串排列
有一道程式設計趣題,
要求判斷一段文字T中,是否包含一個字串W的某種排列。
據說不少公司還用這道題目用來面試程式設計師。題目的答案在網路上也到處都能搜尋到。這道題目存在一個特別優雅的解法,體現了數學同構的優美。
數論中的算術基本定理說:任何一個正整數都可以唯一地表示成若干素數的乘積。我們的思路是,將每一個不同字元對應到一個素數上去,a對應2,b對應 3,c對應5......。這樣任意給定一個字串W,不管它是否包含重複的字元,我們都可以把它表示為素數的乘積:
F = product([primes[c] for c in W]])
我們稱其為字串W的數論指紋F。如果W是空串,我們規定它的指紋等於1。根據整數乘法的交換律,我們知道無論W怎樣排列,其數論指紋都不變,並且根據算術基本定理,這個數論指紋是唯一的。現在我們就得到 了一個特別簡潔的解法:我們首先計算出W的數論指紋F ,然後用一個長度為|W|的視窗沿著TXT從左向右滑動。一開始我們需要計算TXT在這個視窗內的數論指紋,並和F比較,如果相等就說明TXT包含W的某種排列。如果不等我們將這個視窗向右滑動一個字元。此時我們可以非常容易地計算新視窗內的數論指紋:只要把滑出的字元對應的素數除掉,再把滑入的字元對應的素數乘上就可以了。任何時候如果新視窗內的數論指紋等於F,就說明找到了一個排列。當然為了獲得每個不同字元對應的素數,我們還要利用埃拉託斯特尼篩法產生一串素數。下面是一個例子Java程式:
import java.util.stream.LongStream;
import java.util.function.LongPredicate;
public class PermuteSubstr {
private static final int ASCII = 128;
private static LongPredicate sieves = x -> true; // initialize sieve as id
private final static long[] PRIMES = LongStream
.iterate(2, i -> i + 1)
.filter(i -> sieves.test(i)) // Sieve of Eratosthenes
.peek(i -> sieves = sieves.and(v -> v % i != 0)) // update, chain the sieve
.limit(ASCII) // only support ASCII
.toArray();
private static long product(String str) {
return str.chars().mapToLong(c -> PRIMES[c]).reduce(1, (a, b) -> a * b);
}
public static boolean exist(String w, String txt) {
if (w.isEmpty()) {
return true;
}
int m = w.length(), n = txt.length();
if (n < m) {
return false;
}
long target = product(w);
long fp = product(txt.substring(0, m));
for (int i = m; i < n && target != fp; ++i) {
fp = fp / PRIMES[txt.charAt(i - m)] * PRIMES[txt.charAt(i)];
}
return target == fp;
}
}
這段程式中素數序列的產生也很有趣,它利用了埃拉託斯特尼篩法,使用了無窮流的概念,並擷取了前128個素數以處理所有的ASCII碼。關於無窮流可以參見《同構——程式設計中的數學》中“無窮”一章。有關算術基本定理的證明也是數學中優美證明的經典,大數學家柯朗和羅賓的科普讀物《什麼是數學》中有詳細的介紹。
相關文章
- Amazon面試題:尋找最長迴文子串面試題
- 如何高效尋找素數
- 用Go構建你專屬的JA3指紋Go
- 2837 尋找水仙花數
- Android 指紋識別,指紋支付demoAndroid
- 初等數論——同餘
- leetcode 287 尋找重複的數LeetCode
- LeetCode:尋找丟失的數字LeetCode
- abc295D 統計由SS構成的子串個數
- 指紋2
- 指紋3
- 尋找連續或不連續的子字串字串
- 用SVG來找點樂子SVG
- 子串位置
- 尋找真凶
- 端雲互動,裝置指紋的安全進化論
- 指數級加速架構搜尋:CMU提出基於梯度下降的可微架構搜尋方法架構梯度
- win10怎麼加指紋密碼_win10指紋密碼鎖如何新增指紋Win10密碼
- 最長子串
- 子串查詢
- L1-041 尋找250 分數 10
- 在慢變數中尋找小趨勢變數
- 子圖同構之VF2
- 尋找邊境
- 30串聯所有單詞的子串
- CMS指紋識別
- 服務指紋分析
- win10指紋識別登陸怎麼用_win10指紋識別登陸使用教程Win10
- 【leetcode 3149. 找出分數最低的排列】記憶化搜尋LeetCode
- 利用NAS尋找最佳GAN:AutoGAN架構搜尋方案專為GAN打造架構
- 尋找兩個有序陣列的中位數陣列
- 【演算法】數學之旅,根據素數特徵尋找底數演算法特徵
- 最長上升子串
- 子串匹配 BF法
- 尋找Python培訓機構標準是什麼Python
- 162. 尋找峰值
- 08、 尋找周杰倫
- 裝置指紋簡析