[題解] UVa 10855 - Rotated square
Tags
- 題解
- UVa
- E難度
略譯
給你 nn 大方陣、mm 小方陣,方陣中全是大寫字母,
求小方陣順時針轉 0 度、90 度、180 度、270 度後,
在大方陣中出現幾次
題解
暴力。
Hash
一個能夠加速字串比對的方式是:將字串按某個規則轉成整數。
這個動作我們稱作 hash,而相同的東西經過 hash,一定會得到相同的值。
比如說,全大寫字母最常用的:轉 26 進位制
於是線性時間複雜度的兩個字串比對,就會變成常數時間複雜度的兩個整數比較是否相等
假設字串 ABBABBAC 中出現幾次的 ABB
已知 ABB 轉 26 進位後的值是 0x676 + 1x26 + 1 = 27
- ABBABBAC: 27 == 27
- ABBABBAC: 702 != 27
- ABBABBAC: 677 != 27
- ABBABBAC: 27 == 27
- ABBABBAC: 702 != 27
- ABBABBAC: 678 != 27
出現兩次。
二次確認
轉 26 進位,很容易數字就變很大,甚至 int 很快就不夠存。
通常我們直接讓它 overflow,反正 overflow 之後還是會相同。
只是由於 int 能存的值,最多也就 2^32 種;
可能會有不同字串在 overflow 之後,值剛好相同。
但我們可以在 hash 值確定相同後,再真的拿字串去比對以確保它們相同,
仍然能夠省下相當大量的運算。
Rolling Hash
每次重新求一段字串的 hash 相對沒有效率。
但所要求的字串,其實存在相當多的重疊:
- ABBABBAC
- ABBABBAC
- ABBABBAC
於是我們照一般 10 進位制的方法來做:
把不要了的最高位 A 的值刪去 (減去 A x 26^2)
餘下 BB 平移一位數 (乘以 26)
把下一個 A 加進個位數 (加上 A)
於是每個字母只被加進 hash、移出 hash 各一次,
得到一個線性複雜度的方法
雖然 n 比較大時,要刪去的最高位可能是 Z x 26^15 之類的數字,
不過沒關係刪就對了。overflow / underflow 有點像是對 int 範圍取 mod 的概念,
進位後溢出去的位數被捨棄掉。
所以其實加上很大的數,再減回來,還是會維持原本的值。
來自 m 的多重確認
事實上這題不需要二次確認,也可以 AC。
在 m 十分小,小到不足以 overflow 的時候,這方法得出的 hash 值是精確的,
只要沒有 overflow 就沒有不同字串 hash 值互撞的問題。
然而在 m 足夠大的時候,又因為要同時符合 m 個 hash 值相同,才被認定是出現一次,
即使其中數個和不同字串撞值了,只要其它列的 hash 值不相同就沒事。
因此出錯的機率十分低。但仍可能出錯。
以防萬一,二次確認還是比較保險的。
實作提示
- 順時針旋轉 90 度有些複雜,實際把幾個 [i][j] 轉完跑去哪給寫下來,
能幫助思考 mapping。- 180 度就轉兩次,或從轉 90 度的再轉 90 度就好;270 度同理