KMP_字串最小表示_CH1802_Necklace
阿新 • • 發佈:2018-11-09
思路分析:
方法一: 直接使用字串Hash和二分搜尋判斷兩個字串字典序大小關係, 容易給出時間複雜度為O(nlg(n))的解決方案, n為輸入字串的長度, 具體實現見如下AC程式碼:
//CH1802_Necklace #include <cstdio> #include <cstring> using namespace std; const int MAX = 1e6 + 5, P = 1331; char s1[MAX << 1], s2[MAX << 1]; int len; unsigned long long h1[MAX << 1], h2[MAX << 1], ph[MAX << 1]; //如果s[a...a + len - 1] < s[b...b + len - 1]返回true, 否則返回false bool cmp(unsigned long long *s, int a, int b){ //計算最長公共字首 int l = 0, r = len, mid; while(mid = l + r + 1 >> 1, l < r) if(s[a + mid - 1] - s[a - 1] * ph[mid] == s[b + mid - 1] - s[b - 1] * ph[mid]) l = mid; else r = mid - 1; if(l == len) return false; return s[a + l] - s[a + l - 1] * ph[1] < s[b + l] - s[b + l - 1] * ph[1]; } int main(){ scanf("%s %s", s1 + 1, s2 + 1), len = strlen(s1 + 1); memcpy(s1 + len + 1, s1 + 1, len * sizeof(char)) , memcpy(s2 + len + 1, s2 + 1, len * sizeof(char)); ph[0] = 1; for(int i = 1; i <= len * 2; ++i) ph[i] = ph[i - 1] * P; for(int i = 1; i <= len * 2; ++i) h1[i] = h1[i - 1] * P + (s1[i] - '0' + 1), h2[i] = h2[i - 1] * P + (s2[i] - '0' + 1); int ans1 = 1, ans2 = 1; for(int i = 2; i <= len; ++i){ if(cmp(h1, i, ans1)) ans1 = i; if(cmp(h2, i, ans2)) ans2 = i; } if(!strncmp(s1 + ans1, s2 + ans2, len)) printf("Yes\n"), s1[ans1 + len] = '\0', printf("%s\n", s1 + ans1); else printf("No\n"); return 0; }
方法二: 下面先給出AC程式碼, 然後分析程式的正確性及其時間複雜度
//CH1802_Necklace #include <iostream> #include <cstdio> #include <cstring> #include <algorithm> using namespace std; const int MAX = 1e6 + 5; char s[MAX << 1]; int len; char ss[MAX << 1]; int main(){ scanf("%s %s", s + 1, ss + 1), len = strlen(s + 1); memcpy(s + len + 1, s + 1, len), memcpy(ss + len + 1, ss + 1, len); int l = 1, r = 2; while(l <= len && r <= len){ int i = 0; while(i < len && s[l + i] == s[r + i]) ++i; if(i == len) break; if(s[l + i] > s[r + i]){ l = l + i + 1; if(r == l) ++r; } else{ r = r + i + 1; if(l == r) ++l; } } int ans1 = min(l, r); l = 1, r = 2; while(l <= len && r <= len){ int i = 0; while(i < len && ss[l + i] == ss[r + i]) ++i; if(i == len) break; if(ss[l + i] > ss[r + i]){ l = l + i + 1; if(r == l) ++r; } else{ r = r + i + 1; if(l == r) ++l; } } int ans2 = min(l, r); if(!strncmp(s + ans1, ss + ans2, len)){ cout << "Yes" << endl; for(int i = ans1; i <= ans1 + len - 1; ++i) cout << s[i]; cout << endl; } else cout << "No" << endl; return 0; }
設表示將字串S[1...len]的S[i]作為第一個字元的迴圈同構串, 在上述程式碼中, 考慮第14至23行的while迴圈有如下迴圈不變式成立,
在每次第14行迴圈頭檢測之前, 對應字串S的最小表示的, 始終滿足 , 下面證明此迴圈不變式的正確性.
在第一次第14行迴圈頭檢測之前該迴圈不變式顯然成立, 假設第m次第14行迴圈頭檢測之前該不變式成立, 在第m次執行第14至23行迴圈體的過程中, 如果第16行if條件被滿足, 顯然, 設t = min(l, r), p = max(l, r), e = |l - r|, 對任意p <= q <= len, 考慮
如果第16行if條件不成立, 如果S[l + i] > S[r + i], 那麼任意0 <= v <= i, 均有S[(l + v) mod len] > S[(r + v) mod len], 因此l...l + i範圍內的均非最優解, 對於S[r + i] > S[l + i]有完全類似的結論.至此, 只需稍加思考便可知, 對於第m + 1次迴圈頭檢測上述迴圈不變式依然成立.
顯然第15行的while迴圈執行O(len)次, 因此上述程式的時間複雜度為O(len)