求公共前缀长度与所选字符串个数的乘积的最大值 Trie树求最值 UVA 11488 Hyper Prefix Sets
2015-09-24 16:00
330 查看
题目大意:
白书练习题
给定一个字符串集合S, 定义P(S)为所有字符串的公共前缀长度与S中字符串个数的乘积, 例如P{000, 001, 0011} = 6,
现在给出n个只包含字符01的串(n <= 50000), 从中选出一个子集合S, 使得P(S)最大, 求最大值
大致思路:
这题比较巧妙, 刚开始是凭感觉写的, 结果一发AC了...后来证明了一下正确性
我的想法就是对n个字符串插入Trie树的时候, 每插入一个节点, 就对当前节点的值加上其深度, 最后所有节点上的值的最大值就是要求的P(S)
正确性的证明放在代码注释里了, 细节见代码注释吧, 表示这题还真是巧妙=
/*
* 话说我这个做法在写的时候只是个猜想觉得这样可行
* 就是对n个字符串插入Trie树的时候, 每插入一个节点, 就对当前节点的值加上其深度
* 最后所有节点上的值的最大值就是要求的P(S)
* 想这个方法的时候是凭直觉觉得是对的就敲了...结果一发AC了...
* 现在再想一下为什么是对的...
* 首先按照我这个做法, 最后每个节点代表的就是过这个点的所有字符串的数量乘上这个深度
* 首先很明显的是无论这S个字符串按照什么样的顺序插入最后的结果是一样的(Trie一样)
* 那么对于字符串集合T, 如果只插入了T的所有串,如果LCP长度是t, 那么必然存在一个节点其值为t*|T|
* 那么对于所有S的子集, 由于插入顺序与最后结果无关, 而且在插入的时候每个点的值都是随着插入串变多不减的
* 所以最终那个最大值的点的值一定不会被破坏, 所以在所有的串被插入之后
* 如果原问题最优解是选取子集T, 那么LCP(T)*|T|一定存在于最后得到Trie树上的某个节点
* 且那个节点的值一定是最大的
* 还是不懂的话就看感觉吧....
*/
代码如下:
白书练习题
给定一个字符串集合S, 定义P(S)为所有字符串的公共前缀长度与S中字符串个数的乘积, 例如P{000, 001, 0011} = 6,
现在给出n个只包含字符01的串(n <= 50000), 从中选出一个子集合S, 使得P(S)最大, 求最大值
大致思路:
这题比较巧妙, 刚开始是凭感觉写的, 结果一发AC了...后来证明了一下正确性
我的想法就是对n个字符串插入Trie树的时候, 每插入一个节点, 就对当前节点的值加上其深度, 最后所有节点上的值的最大值就是要求的P(S)
正确性的证明放在代码注释里了, 细节见代码注释吧, 表示这题还真是巧妙=
/*
* 话说我这个做法在写的时候只是个猜想觉得这样可行
* 就是对n个字符串插入Trie树的时候, 每插入一个节点, 就对当前节点的值加上其深度
* 最后所有节点上的值的最大值就是要求的P(S)
* 想这个方法的时候是凭直觉觉得是对的就敲了...结果一发AC了...
* 现在再想一下为什么是对的...
* 首先按照我这个做法, 最后每个节点代表的就是过这个点的所有字符串的数量乘上这个深度
* 首先很明显的是无论这S个字符串按照什么样的顺序插入最后的结果是一样的(Trie一样)
* 那么对于字符串集合T, 如果只插入了T的所有串,如果LCP长度是t, 那么必然存在一个节点其值为t*|T|
* 那么对于所有S的子集, 由于插入顺序与最后结果无关, 而且在插入的时候每个点的值都是随着插入串变多不减的
* 所以最终那个最大值的点的值一定不会被破坏, 所以在所有的串被插入之后
* 如果原问题最优解是选取子集T, 那么LCP(T)*|T|一定存在于最后得到Trie树上的某个节点
* 且那个节点的值一定是最大的
* 还是不懂的话就看感觉吧....
*/
代码如下:
#include<cstring> #include<vector> #include<algorithm> #include<iostream> using namespace std; const int maxnode = 4000 * 100 + 10; const int sigma_size = 26; int maxx=-1; // 字母表为全体小写字母的Trie struct Trie { int ch[maxnode][sigma_size]; int val[maxnode]; int sz; // 结点总数 void clear() { sz = 1; memset(ch[0], 0, sizeof(ch[0])); memset(val, 0, sizeof(val));} // 初始时只有一个根结点 int idx(char c) { return c - '0'; } // 字符c的编号 // 插入字符串s,附加信息为v。注意v必须非0,因为0代表“本结点不是单词结点” void insert(const char *s) { int u = 0, n = strlen(s); for(int i = 0; i < n; i++) { int c = idx(s[i]); if(!ch[u][c]) { // 结点不存在 memset(ch[sz], 0, sizeof(ch[sz])); val[sz] = 0; ch[u][c] = sz++; // 新建结点 } u = ch[u][c]; // 往下走 val[u]+=(i+1); maxx=max(maxx,val[u]) ; } return; //val[u]+=n; //maxx=max(maxx,val[u]) ; //val[u] = v; // 字符串的最后一个字符的附加信息为v } }; #include<cstdio> const int maxl = 300000 + 10; // 文本串最大长度 const int maxw = 4000 + 10; // 单词最大个数 const int maxwl = 100 + 10; // 每个单词最大长度 const int MOD = 20071027; int d[maxl], len[maxw], S; char text[maxl], word[maxwl]; Trie trie; int main() { //freopen("in.txt","r",stdin); int tt,S; cin>>tt; while(tt--) { maxx=-1; cin>>S; trie.clear(); for(int i = 1; i <= S; i++) { scanf("%s", word); trie.insert(word); } cout<<maxx<<endl; } return 0; }
相关文章推荐
- iOS 拍照获取照片 翻转90度的问题
- .net3.5后新增的 BeginInvoke EndInvoke 异步操作
- 【九度OJ】题目1204:农夫、羊、菜和狼的故事 -----------状态压缩+搜索
- hdu4746 Mophues
- 个人前博客地址
- ADC 驱动分析
- android4.4以上系统Uri错误的解决方法
- android4.4以上系统Uri错误的解决方法
- SSRF的一些总结
- 纯 AS3 将图片转换为 SWF(转自同行“古树悬叶”的博客)
- [LeetCode]Remove Duplicates from Sorted Array
- Scala学习笔记
- 如何查看手机型号机型和品牌最有效的方法
- 堆和栈的区别
- 验证stderr stdout stdin 缓冲类型和缓冲区大小 以及在重定向后缓冲类型的改变
- 面试---内联函数和宏定义的区别
- HashMap、Hashtable、HashSet的比较
- cocos2d lua 引用protobuf进行通信
- tomcat优化
- SSRF漏洞的挖掘经验