算法练习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

运行结果:

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{@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

运行结果:

2.1.3 结论

当比较策略是个环时,不具有传递性,这个比较策略无效。

例如,有甲、乙、丙三个变量,订制的策略为:

  • 甲和乙:甲在前;
  • 乙和丙:乙在前;
  • 甲和丙:丙在前。

以上策略构成一个环。

要使得比较的结果唯一,则设定的比较策略要具有传递性,即:

  • a.b≤b.a
  • b.c≤c.b
  • 则:a.c≤c.a

以上式子中,将字符串等同于k进制的数:

  • 12连接21的结果为:1221=12*10^{2}+21
  • 即12乘以10(进制)的21长度(2)次方

则字符串拼接可以理解为:

  • a.b=a*k^{b.length}+b

m(b)=k^{b},则上述的传递性规则可表示为:

  • a.b\leq b.a\Leftrightarrow a*m(b)+b\leq b*m(a)+a
  • b.c\leq c.b\Leftrightarrow b*m(c)+c\leq c*m(b)+b

化简:

a*m(b)+b\leq b*m(a)+a \\ \Leftrightarrow a*m(b)\leq b*m(a)+a-b \\ \Leftrightarrow a*m(b)*c\leq b*m(a)*c+a*c-b*c

b*m(c)+c\leq c*m(b)+b \\\Leftrightarrow b*m(c)+c-b\leq c*m(b) \\ \Leftrightarrow b*m(c)*a+c*a-b*a\leq c*m(b)*a

由以上两个式子得:

a*m(b)*c\leq b*m(a)*c+a*c-b*c

b*m(c)*a+c*a-b*a\leq c*m(b)*a

由数的交换律和结合律可知:

a*m(b)*c=c*m(b)*a

所以:

b*m(c)*a+c*a-b*a\leq b*m(a)*c+a*c-b*c

化简结果:

b*m(c)*a+c*a-b*a\leq b*m(a)*c+a*c-b*c \\ \Leftrightarrow b*m(c)*a-b*a\leq b*m(a)*c-b*c \\ \Leftrightarrow m(c)*a-a\leq m(a)*c-c \\ \Leftrightarrow m(c)*a+c\leq m(a)*c+a \\ \Leftrightarrow a.c\leq c.a

得证。

要证明得到的字符串s是最小的字典序,即证明任意交换两个字符,得到的字典序都比s的字典序大。

以得到的最小字典序字符串结果为:...a,m_{1},...,m_{k},b,...,要证明...b,m_{1},...,m_{k},a,...肯定大于等于...a,m_{1},...,m_{k},b,...

在第一个串中,am_{1}的前面,则:a.m_{1}\leq m_{1}.a

若将am_{1}交换:...a,m_{1},...,m_{k},b,...\leq ...m_{1},a,m_{2},...,m_{k},b,...

一直换到ab挨着:

...m_{1},a,m_{2},...,m_{k},b,... \\ \leq ...m_{1},m_{2},a,...,m_{k},b,... \\ \leq ...m_{1},m_{2},...,a,m_{k},b,... \\ \leq ...m_{1},m_{2},...,m_{k},a,b,...

然后将b往前换:

...m_{1},m_{2},...,m_{k},a,b,... \\ \leq ...m_{1},m_{2},...,m_{k},b,a,... \\ \leq ...b,m_{1},m_{2},...,m_{k},a,...

最终:

...m_{1},a,m_{2},...,m_{k},b,... \leq ...b,m_{1},m_{2},...,m_{k},a,...

所以,得到的结果...a,m_{1},...,m_{k},b,...已是最小。


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章