- 给定两个字符串str1和str2,再给定三个整数ic、dc和rc,分别代表插入、删 除和替换一个字符的代价,返回将str1编辑成str2的最小代价。
【举例】
str1="abc",str2="adc",ic=5,dc=3,rc=2 从"abc"编辑成"adc",把'b'替换成'd'是代价最小的,所以返回2
str1="abc",str2="adc",ic=5,dc=3,rc=100 从"abc"编辑成"adc",先删除'b',然后插入'd'是代价最小的,所以返回8
str1="abc",str2="abc",ic=5,dc=3,rc=2 不用编辑了,本来就是一样的字符串,所以返回0
/**
* 给定两个字符串str1和str2,再给定三个整数ic、dc和rc,分别代表插入、删 除和替换一个字符的代价,返回将str1编辑成str2的最小代价。
* 【举例】
* str1="abc",str2="adc",ic=5,dc=3,rc=2 从"abc"编辑成"adc",把'b'替换成'd'是代价最小的,所以返回2
* str1="abc",str2="adc",ic=5,dc=3,rc=100 从"abc"编辑成"adc",先删除'b',然后插入'd'是代价最小的,所以返回8
* str1="abc",str2="abc",ic=5,dc=3,rc=2 不用编辑了,本来就是一样的字符串,所以返回0
*/
public class MinEditorDistance {
public static int minEditorDistance(String str1,String str2,int ic,int dc,int rc){
if(str1 == null && str2 == null){
return 0;
}
// 申请一个dp表dp[i][j]表示,str1有i个字符,str2有j个字符,从str1编辑为str2的最小代价
// 多申请一个位置,i= 0表示str1是空字符串,j=0表示str2是空字符串
char[] c1 = str1.toCharArray();
char[] c2 = str2.toCharArray();
int len1 = c1.length;
int len2 = c2.length;
int[][] dp = new int[len1+1][len2+1];
// 从空字符串变成空字符串代价为0
dp[0][0] = 0;
// 第一行表示str1为空字符串变成str2的字符串需要的代价,必定是一个j倍的插入代价(有几个字符就有几个插入代价)
for (int i = 1; i <= len2; i++) {
dp[0][i] = i * ic;
}
// 第一列表示str1是有i个字符的,要变成str2空字符串需要的代价是i个删除待机
for (int i = 1; i <= len1; i++) {
dp[i][0] = i*dc;
}
// 普通位置的可能性
// 1.str1,str2字符最后一个字符相等,那就看str1的0~i-1,位置变成str2的0~ j-1需要多少代价
// 2.str1,str2字符最后一个字符不相等,需要忍受一个替换代价
// 1.2只会同时满足一个
// 3.用str1 0 ~ i-1,之前的变成str2,最后在删除str1i位置的元素需要一个删除代价
// 4.用str1 0~i位置编辑成str2 0~j-1的字符串,最后在str1i+1位置添加一个,需要一个插入代价
for (int i = 1; i <= len1; i++) {
for (int j = 1; j <= len2; j++) {
if(c1[i-1] == c2[j-1]){
dp[i][j] = dp[i-1][j-1];
}else {
dp[i][j] = dp[i-1][j-1] + rc;
}
int p3 = dp[i-1][j] + dc;
int p2 = dp[i][j-1] + ic;
dp[i][j] = Math.min(dp[i][j],Math.min(p2,p3));
}
}
return dp[len1][len2];
}
}
- 给定两个字符串s1和s2,问s2最少删除多少字符可以成为s1的子串?比如 s1 = "abcde",s2 = "axbc"返回1。s2删掉'x'就是s1的子串了。
/**
* 给定两个字符串s1和s2,问s2最少删除多少字符可以成为s1的子串?
* 比如 s1 = "abcde",s2 = "axbc"
* 返回1。s2删掉'x'就是s1的子串了。
*/
public class DeleteMIinChar {
public static int deleteMinChar(String str1,String str2){
if(str1 == null || str2.length() == 0){
return 0;
}
// 枚举str1所有的子串,看看str2通过最小编辑距离能否成为str1的子串,
// 此最小编辑距离只有删除操作,可以吧删除的代价看做是一
int res = Integer.MAX_VALUE;
for (int i = 0; i < str1.length(); i++) {
// i是子串的开头
for (int j = i+1; j <= str1.length(); j++) {
// 由于用到了substring左闭右开所以子串的结尾是j+1,
// 每个子串取做最小编辑距离
res = Math.min(res,minEditorDistance(str2,str1.substring(i,j),1));
}
}
return res;
}
// 阉割版,只有删除操作
private static int minEditorDistance(String str1,String str2,int dc){
if(str1 == null && str2 == null){
return 0;
}
// 申请一个dp表dp[i][j]表示,str1有i个字符,str2有j个字符,从str1编辑为str2的最小代价
// 多申请一个位置,i= 0表示str1是空字符串,j=0表示str2是空字符串
char[] c1 = str1.toCharArray();
char[] c2 = str2.toCharArray();
int len1 = c1.length;
int len2 = c2.length;
int[][] dp = new int[len1+1][len2+1];
// 从空字符串变成空字符串代价为0
dp[0][0] = 0;
// 第一行表示str1为空字符串变成str2的字符串需要的代价,只有删除操作无法完成
for (int i = 1; i <= len2; i++) {
dp[0][i] = Integer.MAX_VALUE;
}
// 第一列表示str1是有i个字符的,要变成str2空字符串需要的代价是i个删除待机
for (int i = 1; i <= len1; i++) {
dp[i][0] = i*dc;
}
// 普通位置的可能性
// 1.str1,str2字符最后一个字符相等,那就看str1的0~i-1,位置变成str2的0~ j-1需要多少代价
// 2.str1,str2字符最后一个字符不相等,需要忍受一个替换代价
// 1.2只会同时满足一个
// 3.用str1 0 ~ i-1,之前的变成str2,最后在删除str1i位置的元素需要一个删除代价
// 4.用str1 0~i位置编辑成str2 0~j-1的字符串,最后在str1i+1位置添加一个,需要一个插入代价
for (int i = 1; i <= len1; i++) {
for (int j = 1; j <= len2; j++) {
// 假设无法编辑
dp[i][j] = Integer.MAX_VALUE;
if(dp[i-1][j] != Integer.MAX_VALUE){
dp[i][j] = dp[i-1][j] + dc;
}
if(c1[i-1] == c2[j-1] && dp[i-1][j-1] != Integer.MAX_VALUE){
dp[i][j] = Math.min(dp[i][j],dp[i-1][j-1]);
}
}
}
return dp[len1][len2];
}
public static void main(String[] args) {
String str1 = "abc";
String str2 = "avmlakds";
System.out.println(deleteMinChar(str1,str2));
}
}
- 求完全二叉树节点的个数,要求时间复杂度低于O(N)
/**
* 求完全二叉树节点的个数
* 要求时间复杂度低于O(N)
*/
public class NodeNum {
public static int nodeNum(Node head){
if(head == null){
return 0;
}
// 流程,先求树的高度h,然后再求右树的高度r
// 1.如果右树的高度小于深度,那么右树就是一棵满二叉树
// 2.如果右树高度等于深度,则左树是一棵满二叉树,然后递归调用不是满的一遍
return process(head,1,treeH(head,1));
}
// 给你一个节点,和现在的层数,返回node为头的节点个数
private static int process(Node node,int level,int h){
if(level == h){
return 1;
}
int i = treeH(node.right, level + 1);
int sum = 0;
if(i == h){
sum = (1 << (h - level)) + process(node.right,level+1,h);
}else{
sum = (1 << (h - level-1)) + process(node.left,level+1,h);
}
return sum;
}
// 计算node为头的二叉树的高度
private static int treeH(Node node,int level){
if(node == null){
return 0;
}
int l = level;
Node cur = node.left;
while (cur != null){
l++;
cur = cur.left;
}
return l;
}
public static void main(String[] args) {
Node head = new Node(1);
head.left = new Node(2);
head.right = new Node(3);
head.left.left = new Node(4);
head.left.right = new Node(5);
head.right.left = new Node(6);
System.out.println(nodeNum(head));
}
}
public class Code04_LeastRecentlyUsedCache {
public static class Node {
public K key;
public V value;
public Node last;
public Node next;
public Node(K key, V value) {
this.key = key;
this.value = value;
}
}
// 双向链表
// 从head到tail所有节点都是串好的
public static class NodeDoubleLinkedList {
private Node head;
private Node tail;
public NodeDoubleLinkedList() {
head = null;
tail = null;
}
// 如果一个新的节点加入,放到尾巴上去
public void addNode(Node newNode) {
if (newNode == null) {
return;
}
// newNode != null
if (head == null) { // 双向链表中一个节点也没有
head = newNode;
tail = newNode;
} else { // 双向链表中之前有节点,tail(非null)
tail.next = newNode;
newNode.last = tail;
tail = newNode;
}
}
// 潜台词 : 双向链表上一定有这个node
// node分离出,但是node前后环境重新连接
// node放到尾巴上去
public void moveNodeToTail(Node node) {
if (this.tail == node) {
return;
}
if (this.head == node) { // 当前node是老头部
this.head = node.next;
this.head.last = null;
} else { // 当前node是中间的一个节点
node.last.next = node.next;
node.next.last = node.last;
}
node.last = this.tail;
node.next = null;
this.tail.next = node;
this.tail = node;
}
// 把头节点删掉并返回
public Node removeHead() {
if (this.head == null) {
return null;
}
Node res = this.head;
if (this.head == this.tail) { // 链表中只有一个节点的时候
this.head = null;
this.tail = null;
} else {
this.head = res.next;
res.next = null;
this.head.last = null;
}
return res;
}
}
public static class MyCache {
private HashMap> keyNodeMap;
private NodeDoubleLinkedList nodeList;
private final int capacity;
public MyCache(int cap) {
if (cap < 1) {
throw new RuntimeException("should be more than 0.");
}
keyNodeMap = new HashMap>();
nodeList = new NodeDoubleLinkedList();
capacity = cap;
}
public V get(K key) {
if (keyNodeMap.containsKey(key)) {
Node res = keyNodeMap.get(key);
nodeList.moveNodeToTail(res);
return res.value;
}
return null;
}
public void set(K key, V value) {
if (keyNodeMap.containsKey(key)) {
Node node = keyNodeMap.get(key);
node.value = value;
nodeList.moveNodeToTail(node);
} else { // 这是一个新加的记录,有可能出现替换
if (keyNodeMap.size() == capacity) {
removeMostUnusedCache();
}
Node newNode = new Node(key, value);
keyNodeMap.put(key, newNode);
nodeList.addNode(newNode);
}
}
private void removeMostUnusedCache() {
Node removeNode = nodeList.removeHead();
keyNodeMap.remove(removeNode.key);
}
}
public static void main(String[] args) {
MyCache testCache = new MyCache(3);
testCache.set("A", 1);
testCache.set("B", 2);
testCache.set("C", 3);
System.out.println(testCache.get("B"));
System.out.println(testCache.get("A"));
testCache.set("D", 4);
System.out.println(testCache.get("D"));
System.out.println(testCache.get("C"));
}
}
- 给定两个字符串,记为start和to,再给定一个字符串列表list,list中一定包含to list中没有重复字符串,所有的字符串都是小写的。
规定: start每次只能改变一个字符,最终的目标是彻底变成to,但是每次变成的新字符串必须在list 中存在。
请返回所有最短的变换路径。
【举例】
start="abc",end="cab",list={"cab","acc","cbc","ccc","cac","cbb","aab","abb"}
转换路径的方法有很多种,但所有最短的转换路径如下:
abc -> abb -> aab -> cab
abc -> abb -> cbb -> cab
abc -> cbc -> cac -> cab
abc -> cbc -> cbb -> cab
流程: 生成每个字符串的邻居字符串列表,邻居字符串的定义是:一个字符串通过改变一个字符就可以变成另一个字符串,那么另一个字符串就是这个字符的邻居。
/**
* 给定两个字符串,记为start和to,再给定一个字符串列表list,list中一定包含to list中没有重复字符串,所有的字符串都是小写的。
* 规定: start每次只能改变一个字符,最终的目标是彻底变成to,但是每次变成的新字符串必须在list 中存在。
* 请返回所有最短的变换路径。
* 【举例】
* start="abc",end="cab",list={"cab","acc","cbc","ccc","cac","cbb","aab","abb"}
* 转换路径的方法有很多种,但所有最短的转换路径如下:
* abc -> abb -> aab -> cab
* abc -> abb -> cbb -> cab
* abc -> cbc -> cac -> cab
* abc -> cbc -> cbb -> cab
*/
public class MinPath {
public static List> minPath(String start,String to,List list){
if(list == null || !list.contains(to)){
return null;
}
HashMap> stringListHashMap = neighborList(list);
HashMap distance = distance(start, stringListHashMap);
LinkedList pathList = new LinkedList<>();
List> res = new ArrayList<>();
getShortestPaths(start,to,stringListHashMap,distance,pathList,res);
return res;
}
// 邻居表
private static HashMap> neighborList(List list){
// 生成字符串的邻居表
HashMap> neighborList = new HashMap<>();
for (String item : list){
// 每个字符每个位置都在a~z中变换,看list中是否存在,存在则这个字符串就是他的邻居
char[] c = item.toCharArray();
List temp = new ArrayList<>();
for (int i = 0; i < c.length; i++) {
for (char j = 'a'; j <= 'z' ; j++) {
if(c[i] != j){
char o = c[i];
c[i] = j;
String s = String.valueOf(c);
if(list.contains(s)){
temp.add(s);
}
c[i] = o;
}
}
}
neighborList.put(item,temp);
}
return neighborList;
}
// 生成一个距离表,表示每个字符串到start字符春的距离
private static HashMap distance(String start,HashMap> map){
Set set = new HashSet<>();
Queue queue = new LinkedList();
queue.offer(start);
HashMap distance = new HashMap<>();
distance.put(start,0);
set.add(start);
while (!queue.isEmpty()){
String poll = queue.poll();
List list = map.get(poll);
for (String item : list){
if(!set.contains(item)){
distance.put(item,distance.get(poll) + 1);
queue.offer(item);
set.add(item);
}
}
}
return distance;
}
private static void getShortestPaths(
String cur, String to,
HashMap> nexts,
HashMap distances,
LinkedList path,
List> res) {
path.add(cur);
if (to.equals(cur)) {
res.add(new LinkedList(path));
} else {
for (String next : nexts.get(cur)) {
if (distances.get(next) == distances.get(cur) + 1) {
getShortestPaths(next, to, nexts, distances, path, res);
}
}
}
path.pollLast();
}
}