算法练习day13——190401(前缀树、贪心策略拼接字符串使字典序最小)
1.前缀树(Trie Tree)
1.1 字符串生成前缀树的过程
字母是填在路上的,不是填在节点上的。
首先是一个空的头结点:
加入“abc”到这棵树中:
头结点有到a的路吗?没有,添加:
有a到b的路吗?没有,添加,c也一样:
接着添加字符串“bce”:
添加字符串“abd”:
添加字符串“bef”:
在添加N个字符串之后,可以查询,“某个字符串是否是以“be”开始的”。
若要查询“是否含有字符串“be”?”,原始的树结构,不足以实现这个功能。
- 添加了“bef”,但是“be”的路径和“bef”的路径是重合的,无法判断有没有加过“be”。
需在节点处添加一个值,表示有多少个字符串是以这个字符结尾的。更改之后的树结构如下图:
此时b-e中e后面的节点值为0,说明没有加过"be"。
此树结构还可以查某个字符串加过几次。
“给出有多少个字符串以给定的额字符串作为前缀”——需要再加一个数据项:每一个节点被划过了多少次。
上述树结构,更改之后如下图所示:
添加字符串的代价:字符串本身的长度,和数据量N无关;
查某个字符的代价:字符串本身的长度,和数据量N无关;
1.2 代码实现
package Tree;public class TrieTree {public static void main(String[] args) {Trie trie = new Trie();System.out.println(trie.search("hello"));trie.insert("hello");System.out.println(trie.search("hello"));trie.delete("hello");System.out.println(trie.search("hello"));trie.insert("hello");trie.insert("hello");trie.delete("hello");System.out.println(trie.search("hello"));trie.delete("hello");System.out.println(trie.search("hello"));trie.insert("helloa");trie.insert("helloac");trie.insert("helloab");trie.insert("helload");trie.delete("helloa");System.out.println(trie.search("helloa"));System.out.println(trie.prefixNumber("hello"));}public static class TrieNode{public int path;public int end;public TrieNode[] nexts;//此节点有多少条通往下层的路//通过判断此节点的路径指向的节点是否为空来判断这条路是否存在public TrieNode() {this.path=0;this.end=0;this.nexts=new TrieNode[26];//26个字母就26条路}}public static class Trie{TrieNode root;public Trie() {root=new TrieNode();}public void insert(String str) {if(str==null)return;char[] arr=str.toCharArray();TrieNode node=root;int index=0;for(int i=0;i<arr.length;i++) {index=arr[i]-'a';//0~25if(node.nexts[index]==null)node.nexts[index]=new TrieNode();node=node.nexts[index];//node往下挑跳一个,i=0时是从头结点跳到第一个节点node.path++;}node.end++;//整个字符串加完之后,最后一个字符的end+1;}public int search(String str) {if(str==null)return 0;char[] arr=str.toCharArray();TrieNode node=root;int index=0;for(int i=0;i<arr.length;i++) {index=arr[i]-'a';//0~25if(node.nexts[index]!=null)node=node.nexts[index];elsereturn 0;}return node.end;}public void delete(String str) {if(search(str)==0)return;char[] arr=str.toCharArray();TrieNode node=root;int index=0;for(int i=0;i<arr.length;i++) {index=arr[i]-'a';//0~25if(--node.nexts[index].path==0) {//判断是否需要断掉这条路node.nexts[index]=null;return;}elsenode=node.nexts[index];}node.end--;}public int prefixNumber(String str) {if(str==null)return 0;char[] arr=str.toCharArray();TrieNode node=root;int index=0;for(int i=0;i<arr.length;i++) {index=arr[i]-'a';//0~25if(node.nexts[index]!=null)node=node.nexts[index];elsereturn 0;}return node.path;}}
}
运行结果:
2.贪心策略
(使用对数器严重贪心策略是否正确,不要纠结正确性证明)
贪心:根据某个标准分出个优先级,然后按照优先级的顺序执行。
2.1 举例
拼接字符串,使得结果为最小(按字典序)。
比如:"ab"、“cd”、"ef"三个字符串,拼接的结果可以由多种,但是”abcdef“这个结果才是最小的,题目中想要的。
2.1.1 分析
若先将字符串数组中的字符串拍好序再进行拼接,如下:
if(str1<=str2)str1连接str2;
elsestr2连接str1;
这种方式不对,因为:"b"<"ba",但是拼接之后"bba">"bab"。
所以正确的比较策略应该是:
if(str1连接str2<=str2连接str1)str1放前面;
elsestr2放前面;
即,谁作为前缀更小谁放前面。
2.1.2 代码实现
package Solution;import java.util.Arrays;
import java.util.Comparator;public class StrCon {public static void main(String[] args) {String[] strs1 = { "jibw", "ji", "jp", "bw", "jibw" };System.out.println(lowestString(strs1));String[] strs2 = { "ba", "b" };System.out.println(lowestString(strs2));}public static class MyComparator implements Comparator<String>{@Overridepublic int compare(String str1, String str2) {return (str1+str2).compareTo(str2+str1);/** compareTo* 如果指定的数与参数相等返回0。* 如果指定的数小于参数返回 -1。* 如果指定的数大于参数返回 1。*/} }public static String lowestString(String[] strs) {if(strs==null||strs.length==0)return " ";Arrays.sort(strs, new MyComparator());String res=" ";for(int i=0;i<strs.length;i++) res+=strs[i];return res;}
}
运行结果:
2.1.3 结论
当比较策略是个环时,不具有传递性,这个比较策略无效。
例如,有甲、乙、丙三个变量,订制的策略为:
- 甲和乙:甲在前;
- 乙和丙:乙在前;
- 甲和丙:丙在前。
以上策略构成一个环。
要使得比较的结果唯一,则设定的比较策略要具有传递性,即:
- a.b≤b.a
- b.c≤c.b
- 则:a.c≤c.a
以上式子中,将字符串等同于k进制的数:
- 连接的结果为:
- 即12乘以10(进制)的21长度(2)次方
则字符串拼接可以理解为:
令,则上述的传递性规则可表示为:
化简:
由以上两个式子得:
由数的交换律和结合律可知:
所以:
化简结果:
得证。
要证明得到的字符串s是最小的字典序,即证明任意交换两个字符,得到的字典序都比s的字典序大。,
以得到的最小字典序字符串结果为:,要证明肯定大于等于
在第一个串中,在的前面,则:
若将和交换:
一直换到和挨着:
然后将b往前换:
最终:
所以,得到的结果已是最小。
算法练习day13——190401(前缀树、贪心策略拼接字符串使字典序最小)相关推荐
- 算法补天系列之——前缀树+贪心算法
介绍前缀树 经典的前缀树,字符都是在路上的,节点是为了边的存在而存在的,有路就可以复用. 那么前缀树对应的数据结构是什么样的呢? 对于节点构建结构,设置一个int pass(表示路径途径这个节点的次数 ...
- 8-详解前缀树贪心算法N皇后问题
一.何为前缀树?如何生成前缀树? 经典的前缀树:点上没有数据,如果有路就复用,没有路就新建. 前缀树点的结构: pass:代表这个点经过了多少次,根节点的pass代表有多少个字符串前缀为空,或者一共加 ...
- 算法入门篇七 前缀树
牛客网 左程云老师的算法入门课 找二叉树的节点的后继节点 原则 如果节点有右子树,那么后继节点就是右子树的最左边的第一个节点 如果节点没有右子树,如果节点是父节点的右孩子,就继续往上找,直到找到一个父 ...
- 左程云算法 day8 前缀树和贪心算法
前缀树 :建立好前缀树后可以把下面的题目快速地解决,便于实现前缀查找 题目:一个字符串类型的数组arr1,另一个字符串类型的数组arr2.arr2中有哪些字符,是arr1中出现的,请打印.arr2中有 ...
- 贪心策略--16经典问题总结!
贪心算法 编号 题目 1货郎担 问题: 货郎担问题:假定有五个城市,已知费用矩阵如下,分别从五个城市出发,然后选取一条费用最小的线路,验证这种算法不能得到最优解. 贪心选择:每次选择之前没有走过的费用 ...
- 前缀树是什么 前缀树的使用场景
前缀树的概述 前缀树又名字典树,单词查找树,Trie树,是一种多路树形结构,是哈希树的变种,和hash效率有一拼,是一种用于快速检索的多叉树结构. 典型应用是用于统计和排序大量的字符串(但不仅限于字符 ...
- C++简单实现 前缀树
今天在leetcode上面看了一道题(第208题),问题是如何实现一个字符串前缀树,能够实现字符串的插入,查找和查找前缀功能.在这里记录一下前缀树的实现: class Trie { public:/* ...
- 前缀树(java实现)
package class_07; /*** * 前缀树** 例子:* 一个字符串类型的数组arr1,另一个字符串类型的数组arr2.* arr2中有哪些字符,是arr1中出现的?请打印* arr2中 ...
- 数据结构与算法笔记:贪心策略之BSTBBST, Hashtable+Dictionary+Map, Priority Queue~Heap, Minium Spanning Tree
BST & BBST BST(Binary Search Tree) 二叉搜索树,也就是使用二叉树来做查找 BBST(Balanced Binary Search Tree) 平衡二叉搜索树 ...
最新文章
- snmpd 子代理模式编译测试
- java 取上下文路径_取Servlet上下文路径,取WebContent的路径
- Spring Cloud Feign如何实现JWT令牌中继以传递认证信息
- JSONP解决跨域及ajax同步问题
- Marketing Cloud contact主数据的csv导入
- python读取html_从零开始的Python爬虫教程(一):获取HTML文档
- 怎么看电脑电源多少w_UPS不间断电源设备哪个品牌好?如何选购家用电脑UPS电源?UPS电源价格多少?...
- 【Python】pdf2image模块+poppler将PDF转换为图片
- 检索COM类工厂组件的CLSID 80040154(不容易啊!!) 80040154.
- storagesky存储天地
- UINavigationController_学习笔记
- Oracle 安装 RAC 11.2.0.4 centos7.4 -udev磁盘绑定/执行root脚本报错
- 系统架构图怎么画_产品架构图到底是怎么“画”出来的?
- 360安全浏览器安装adblock plus
- Memory Limit Exceeded
- 在windows平台上打造Linux开发环境-洋葱先生-杨少通
- 北大的戴威,为何输给了三本的胡玮炜?
- Meta 将使用微软 Azure 最新虚拟机 (VM) 系列,多达 5400 个 GPU
- opengl-PBR基于物理的渲染(Physically Based Rendering):理论基础
- sql server 不允许保存更改,您所做的更改要求删除并重新创建以下表 的解决办法