这几天到处赠饭领红包,感觉脸都笑得僵硬了。。。在家好没状态,还是早点去学校算了*_*
题意: 输入n个字符串(有大小写字母和数字),两两调用一次strcmp(),一共调用 n*(n+1) 次,问字符比较的总次数是多少?
int strcmp(char *s, char *t)
{
int i;
for (i=0; s[i]==t[i]; i++)
if (s[i]==' ')
return 0;
return s[i] - t[i];
}
注 :s[i]==t[i] 和 s[i]==' ' 各有一次比校。
tags:明显的字典树,边插入边统计就好了
#include<bits/stdc++.h> using namespace std; #pragma comment(linker, "/STACK:102400000,102400000") #define rep(i,a,b) for (int i=a; i<=b; ++i) #define per(i,b,a) for (int i=b; i>=a; --i) #define mes(a,b) memset(a,b,sizeof(a)) #define INF 0x3f3f3f3f #define MP make_pair #define PB push_back #define fi first #define se second typedef long long LL; const int N = 5000005; struct Trie { char ch[N]; int sz, son[N], bro[N]; int tot[N]; void Init() { sz=son[0]=bro[0]=tot[0]=0; } LL Insert(char* str) { LL ret = 0; ++tot[0]; int len=strlen(str), u=0, to; for(int i=0; i<=len; ++i) { for(to=son[u]; to; to=bro[to]) if(ch[to]==str[i]) break; if(to==0) { to=++sz; tot[to]=0; son[to]=0, ch[to]=str[i]; bro[to]=son[u], son[u]=to; } else { ret += 1LL*tot[to]*2; /// } ++tot[to]; ret += tot[u]-tot[to]; /// u = to; } return ret; } } trie; char si[1005]; int main() { int cas = 0, n; while(~scanf("%d", &n) && n) { trie.Init(); LL ans = 0; rep(i,1,n) { scanf("%s", si); ans += trie.Insert(si); } printf("Case %d: %lld ", ++cas, ans); } return 0; } /* 3 that than thae 2 there the */
一般的字典树题目,我们习惯用双数组解决。但是当字符集很大的时候用双数组的形式很有可能会超内存,这个时候就要用 左儿子右兄弟 的形式储存 trie 树。左儿子右兄弟的形式可以有效地节约内存,而且初始化很快,但是插入和询问是 O(n*log(n)), 算是以时间换取空间。
Trie 左儿子右兄第模板:
// Trie 左儿子右兄弟模板 const int N = 5000005; struct Trie { char ch[N];// ch[i]为第i个结点上的字符 int sz, son[N], bro[N]; // 结点总数、左儿子编号、右兄弟编号 int tot[N]; // tot[i]为第i个结点为根的子树包含的叶子结点总数 void Init() { sz=son[0]=bro[0]=tot[0]=0; } void Insert(char* str) { ++tot[0]; int len=strlen(str), u=0, to; for(int i=0; i<len; ++i) { for(to=son[u]; to; to=bro[to]) if(ch[to]==str[i]) break; if(to==0) // 没有找到就新建结点 { to=++sz, tot[to]=0; son[to]=0, ch[to]=str[i]; bro[to]=son[u], son[u]=to; //插入进去 } ++tot[to]; u = to; } } } trie;