標籤:
題意:給定strcmp函數,輸入n個字串,讓你用給定的strcmp函數判斷字元比較了多少次。
析:題意不理解的可以閱讀原題https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=2832
字串很多,又很長,如果按照題目的意思兩兩比較,肯定會TLE,所以要用首碼樹(Trie)來解決,當然只是用簡單的首碼樹也會TLE的,
我們必須對其進行最佳化,看了網上大牛們的,知道要用左兒子右兄弟的方法來最佳化,說實話,以前還沒用過,看了好久才明白是什麼個意思,
不是很明白的畫個圖想一想就會明白的,我採用的是邊插入邊計算的方法,在每一個分枝都進行計算,然後加和,我們要找每個分枝,
除了最後一個都是2*i+1,最後一個再加上2*i+2。
代碼如下:
#include <cstdio>#include <iostream>#include <cstring>using namespace std;typedef long long LL;const int maxn = 4000 * 1000 + 10;LL ans = 0;char s[1010];struct Trie{ int lson[maxn]; int rbro[maxn]; int val[maxn]; char ch[maxn]; int sz; void clear(){ sz = 1; lson[0] = rbro[0] = val[0] = 0; ans = 0; } void insert(const char *s){ int u = 0, v, n = strlen(s); for(int i = 0; i <= n; ++i){ for(v = lson[u]; v; v = rbro[v]) if(s[i] == ch[v]) break;//找到結點 if(!v){//建立結點 v = sz++; ch[v] = s[i]; lson[v] = 0;//左兒子為空白 rbro[v] = lson[u];//結點放在首部 val[v] = 0;//初始化 lson[u] = v;//插入結點 } ans += (val[u]-val[v]) * (2 * i + 1);//(val[u]-val[v])意思是和v不一樣的單詞數 if(i == n){ ans += val[v] * (2 * i + 2); ++val[v]; } ++val[u]; u = v; } }};Trie trie;int main(){ int n, kase = 0; while(scanf("%d", &n), n){ trie.clear(); for(int i = 0; i < n; ++i){ scanf("%s", s); trie.insert(s); } printf("Case %d: %lld\n", ++kase, ans); } return 0;}
下面是大牛Rujia Liu的代碼,我基本是根據他的代碼寫的(因為自己確實是寫不出來。。。)
// UVa11732 strcmp() Anyone?// Rujia Liu#include<cstring>#include<vector>using namespace std;const int maxnode = 4000 * 1000 + 10;const int sigma_size = 26;// 字母表為全體小寫字母的Triestruct Trie { int head[maxnode]; // head[i]為第i個結點的左兒子編號 int next[maxnode]; // next[i]為第i個結點的右兄弟編號 char ch[maxnode]; // ch[i]為第i個結點上的字元 int tot[maxnode]; // tot[i]為第i個結點為根的子樹包含的葉結點總數 int sz; // 結點總數 long long ans; // 答案 void clear() { sz = 1; tot[0] = head[0] = next[0] = 0; } // 初始時只有一個根結點 // 插入字串s(包括最後的‘\0‘),沿途更新tot void insert(const char *s) { int u = 0, v, n = strlen(s); tot[0]++; for(int i = 0; i <= n; i++) { // 找字元a[i] bool found = false; for(v = head[u]; v != 0; v = next[v]) if(ch[v] == s[i]) { // 找到了 found = true; break; } if(!found) { v = sz++; // 建立結點 tot[v] = 0; ch[v] = s[i]; next[v] = head[u]; head[u] = v; // 插入到鏈表的首部 head[v] = 0; } u = v; tot[u]++; } } // 統計LCP=u的所有單詞兩兩的比較次數之和 void dfs(int depth, int u) { if(head[u] == 0) // 葉結點 ans += tot[u] * (tot[u] - 1) * depth; else { int sum = 0; for(int v = head[u]; v != 0; v = next[v]) sum += tot[v] * (tot[u] - tot[v]); // 子樹v中選一個串,其他子樹中再選一個 ans += sum / 2 * (2 * depth + 1); // 除以2是每種選法統計了兩次 for(int v = head[u]; v != 0; v = next[v]) dfs(depth+1, v); } } // 統計 long long count() { ans = 0; dfs(0, 0); return ans; }};#include<cstdio>const int maxl = 1000 + 10; // 每個單詞最大長度int n;char word[maxl];Trie trie;int main() { int kase = 1; while(scanf("%d", &n) == 1 && n) { trie.clear(); for(int i = 0; i < n; i++) { scanf("%s", word); trie.insert(word); } printf("Case %d: %lld\n", kase++, trie.count()); } return 0;}
UVa 11732 "strcmp()" Anyone? (左兒子右兄弟首碼樹Trie)