P5829 【模板】失配樹
阿新 • • 發佈:2020-11-15
目錄
雖然不知道失配樹是什麼,看這題是為數不多的KMP練手的"能做的題"之一,就把這道題切了
題目
個人覺得題目已經非常簡潔明瞭,感動,就不再重複題意
思路
KMP+LCA
先推一道題,對這題應該有幫助:傳送門
KMP中,next[i]
是由next[1~i-1]
得到的,若next[i]
由next[j]
得到,我們看成i是j的子節點next陣列求解完成後,我們就有了一棵樹(這就是標籤"樹形結構"的原因)
題目要我們求的是某兩個字首的最長公共"border",其實就是求連個點的LCA,自己仔細想想
簡單寫一個倍增LCA,滿分程式碼就出來了
關於KMP+倍增的事情這裡不詳細講,在上面推的題有提到
這道題其實真的不難,應該是我做過為數不多的紫題裡最水的一道,其實跟上面推的那道題是一個檔次的
程式碼
直接摳了P3435的程式碼改一改就交了
#include <iostream> #include <cmath> #include <cstdio> #define nn 1000010 #define rr register #define ll long long using namespace std; int sread(char *s) { int siz = 1; do s[siz] = getchar(); while(s[siz] < 'a' || s[siz] > 'z'); while(s[siz] >= 'a' && s[siz] <= 'z') s[++siz] = getchar(); siz--; return siz; } int read() { char c = getchar(); int re = 0; while(c < '0' || c > '9')c = getchar(); while(c >= '0' && c <= '9') re = (re << 1) + (re << 3) + c - '0', c = getchar(); return re; } int next[nn][30] ; int siz[nn]; int dep[nn]; char s[nn]; int n , m; int main() { // freopen("P3435_3.in" , "r" , stdin); n = sread(s); next[1][0] = 0; dep[0] = 0; dep[1] = 1; for(int i = 2 , j = 0 ; i <= n ; i++) { while(j != 0 && s[i] != s[j + 1]) j = next[j][0]; if(s[j + 1] == s[i]) ++j; next[i][0] = j; dep[i] = dep[j] + 1; } /* for(int i = 1 ; i <= n ; i++) cout << dep[i] << '\t'; return 0;*/ int k = log(n) / log(2) + 1; for(int j = 1 ; j <= k ; j++) for(int i = 1 ; i <= n ; i++) { next[i][j] = next[next[i][j - 1]][j - 1]; if(next[i][j] == 0) siz[i] = j; } m = read(); while(m--) { int u = next[read()][0] , v = next[read()][0];//注意這裡一個小細節 // cout << u << '\t' << v << endl; if(dep[u] > dep[v]) { int tmp = u ; u = v ; v = tmp; } int t = log(dep[v]) / log(2); for(int i = t ; i >= 0 ; i--) if(dep[next[v][i]] >= dep[u]) v = next[v][i]; if(u == v) { printf("%d\n" , u); continue; } t = log(dep[v]) / log(2); for(int i = t ; i >= 0 ; i--) if(next[v][i] != next[u][i]) u = next[u][i], v = next[v][i]; printf("%d\n" , next[u][0]); } return 0; }