最长重复子串
中心法:和回文子串一样
//对具体的起点与长度,确认字符串是否为重复拼接形式
bool check(string& a, int len, int begin) {
for(int i = begin; i < len + begin; ++i) {
if(a[i] != a[i + len]) {
//这说明字符串肯定不存在重复拷贝在其后面,返回false
return false;
}
}
return true;//这说明在这个字符串后面存在它的拷贝
}
int solve(string a) {
int n = a.length();
for(int i = n / 2; i > 0; --i) {//枚举长度
for(int j = 0; j <= n - i - i; ++j) {//枚举起点
//起点要保证两个字符串拼接后长度不会大于原字符串
if(check(a, i, j)) {
return 2 * i;//说明找到了重复字符串,长度即为枚举长度两边
}
}
}
return 0;
}
最长回文子串
两种方法:1.中心法:递归调用:只有两种可能性,一种中点和右边的相等,二种中间的左右相等。
2.动态规划:创建一个二维数组:没学会
public int fun(String A, int begin, int end) {
while (begin >= 0 && end < A.length() && A.charAt(begin) == A.charAt(end)) {
begin --;
end ++;
}
return end - begin-1;
}
public int getLongestPalindrome (String A) {
// write code here
int max = 0;
for (int i = 0; i < A.length(); i++) {
max = Math.max(max, Math.max(fun(A, i, i), fun(A, i, i + 1)));
}
return max;
}
NC127 最长公共子串
动态规划:用一个数组保存字符串到ij位置时,前面保存的长度,如果相等,就+1;不等就重新开始计算;
这里要非常注意:
1.数组第0行、0列保存的都是0 因为前面没有比较的值,所有前面统一为0;
2.first保存当前位置,所有first-最大值时等于起始位置
public static String LCS (String str1, String str2) {
// write code here
if (str1.length() == 0 || str2.length() == 0) {
return null;
}
int[][] dp = new int[str1.length() + 1][str2.length() + 1];
int max = 0;
int first = 0;
for (int i = 0; i < str1.length()+1; i++) {
for (int j = 0; j < str2.length()+1; j++) {
if (str1.charAt(i - 1) == str2.charAt(j - 1)) {
dp[i][j] = dp[i - 1][j - 1] + 1;
} else {
dp[i][j] = 0;
}
if (dp[i][j] > max) {
max = dp[i][j];
first = i;
}
}
}
return str1.substring(first - max, first);
}
空瓶问题(以前做过,现在笔都不会动了)
某商店规定:三个空汽水瓶可以换一瓶汽水,允许向老板借空汽水瓶(但是必须要归还)。
小张手上有n个空汽水瓶,她想知道自己最多可以喝到多少瓶汽水
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
// 注意 hasNext 和 hasNextLine 的区别
while (in.hasNextInt()) { // 注意 while 处理多个 case
int a = in.nextInt();
int res = 0;
if(a==0)
break;
while(a/3>0){
res = a/3+res;
a = a/3+a%3;
if(a==2)
a++;
}
System.out.print(res+"\n");
}
}
BM50 两数之和( hash表 )
hash表的遍历时间复杂度只需要:O(1),减少查询时间,防止超时
两个方法:
1.常用的双重循环:
代码都和答案写得一样,能力有所提升
public static int[] twoSum (int[] numbers, int target) { //超时的方法和答案一模一样
// write code here
int n = numbers.length;
int[] res = new int[2];
for(int i =0;i<n;i++){
for(int j =i+1;j<n;j++){
if(numbers[i]+numbers[j] == target)
{
res[0]=i;
res[1]=j;
return res;
}
}
}
return res;
}
2.hash表替换第二重循环
因hash表查询只需要O(1),因此,创建一个hashmap保存数据和对应的下标,故一遍便利即可完成任务
public static int[] twoSum (int[] numbers, int target) {
// write code here
HashMap<Integer,Integer> hashMap = new HashMap<Integer, Integer>();
int[] res = new int[2];
for(int i =0;i<numbers.length;i++){
if(hashMap.containsKey(target-numbers[i])) {
res[0] = hashMap.get(target - numbers[i]) + 1;
res[1] = i + 1;
return (res);
}
hashMap.put(numbers[i],i);
}
return res;
}
ArrayDeque:BM45 滑动窗口的最大值
ArrayDeque是 Deque接口的一个实现,使用了可变数组,所以没有容量上的限制。同时, ArrayDeque是线程不安全的,在没有外部同步的情况下,不能再多线程环境下使用。
ArrayDeque是 Deque的实现类,可以作为栈来使用,效率高于 Stack;也可以作为队列来使用,效率高于 LinkedList。
ArrayDeque 是 Java 集合中双端队列的数组实现,双端队列的链表实现(LinkedList)
需要注意的是, ArrayDeque不支持 null值。
常用方法:addFirst(E e)在数组前面添加元素+pollFirst()删除第一个元素,并返回删除元素的值,如果元素为null,将返回null
1.添加元素
addFirst(E e)在数组前面添加元素
addLast(E e)在数组后面添加元素
offerFirst(E e) 在数组前面添加元素,并返回是否添加成功
offerLast(E e) 在数组后天添加元素,并返回是否添加成功
2.删除元素
removeFirst()删除第一个元素,并返回删除元素的值,如果元素为null,将抛出异常
pollFirst()删除第一个元素,并返回删除元素的值,如果元素为null,将返回null
removeLast()删除最后一个元素,并返回删除元素的值,如果为null,将抛出异常
pollLast()删除最后一个元素,并返回删除元素的值,如果为null,将返回null
removeFirstOccurrence(Object o) 删除第一次出现的指定元素
removeLastOccurrence(Object o) 删除最后一次出现的指定元素
3.获取元素
getFirst() 获取第一个元素,如果没有将抛出异常
getLast() 获取最后一个元素,如果没有将抛出异常
4.队列操作
add(E e) 在队列尾部添加一个元素
offer(E e) 在队列尾部添加一个元素,并返回是否成功
remove() 删除队列中第一个元素,并返回该元素的值,如果元素为null,将抛出异常(其实底层调用的是removeFirst())
poll() 删除队列中第一个元素,并返回该元素的值,如果元素为null,将返回null(其实调用的是pollFirst())
element() 获取第一个元素,如果没有将抛出异常
peek() 获取第一个元素,如果返回null
5.栈操作
push(E e) 栈顶添加一个元素
pop(E e) 移除栈顶元素,如果栈顶没有元素将抛出异常
6.其他
size() 获取队列中元素个数
isEmpty() 判断队列是否为空
iterator() 迭代器,从前向后迭代
descendingIterator() 迭代器,从后向前迭代
contain(Object o) 判断队列中是否存在该元素
toArray() 转成数组
clear() 清空队列
clone() 克隆(复制)一个新的队列
public static ArrayList<Integer> maxInWindows (int[] num, int size) {
// write code here
ArrayDeque<Integer> deque = new ArrayDeque<Integer>();//注意:队列中保存的是下标
ArrayList<Integer> res = new ArrayList<Integer>();//保存结果数组
if(num.length < size || size==0)
return res;
//先放一个滑块
for(int i =0;i<size;i++){
while (!deque.isEmpty() && num[deque.peekLast()]<num[i])
deque.pollLast();
deque.add(i);
}
//循环完成数组
for(int i =size;i<num.length;i++){
res.add(num[deque.peekFirst()]);
//新加一个值,将原有队列中比其小的值删除
while(!deque.isEmpty() && num[deque.getLast()]<num[i])
deque.pollLast();
//将已过期的数据删除/此处比较的是下标
while(!deque.isEmpty() && deque.getFirst()<(i-size+1))
deque.pollFirst();
//很重要,将下标加入队列,不然怎么往后找大的呢?
deque.add(i);
}
res.add(num[deque.peekFirst()]);
return res;
}
双指针+哈希表(滑动窗口)--最长不重复子序列
建一个哈希表,如果这个字段可以放进去保存字段最新出现的位置
j = Math.max(j,hashMap.get(s.charAt(i)));
保存最近的重复字段位置。
public static int lengthOfLongestSubstring(String s) {
HashMap<Character,Integer> hashMap = new HashMap();
int j = -1;
int res = 0;
for(int i =0;i<s.length();i++){
//此判断语句:如果存在这个key就说明有重复,那么就重新,从上一个重复的字段开始算
if(hashMap.containsKey(s.charAt(i))){
j = Math.max(j,hashMap.get(s.charAt(i)));
}
更新保存字符出现的最新位置
hashMap.put(s.charAt(i),i);
计算保存最长的不重复子串
res = Math.max(res,i-j);
}
return res;
}
2.IBM18:二分查找:有序二维数组查到对应数据
上下有序,左右有序:最大特点:左下都比上面的大,右上都比左边的小;左上为最小值,右下为最大值;
思路:选取左下为起点,如果比目标值大,则往上查询(因起点行肯定都比目标值大),如果比目标值小,则往右查询(因起点列肯定都比目标值小);直到找到目标值,或者找到边界。
int h = array.length;
int l = array[0].length;
if(h==0||l==0)
return false;
for(int i=h-1, j=0;j<l && i>=0;){
if(array[i][j]>target)
i--;
else if(array[i][j]<target)
j++;
else
return true;
}
return false;