演算法競賽入門讀書筆記(自用,慎入)
阿新 • • 發佈:2018-12-22
例3.2最長迴文子串
書中講的很好,主要難點在三處
1. 輸入字串要含有空格,對應java下的readLine()方法即可
2. 查找回文串,暴力搜尋,書中從中間搜尋,沒感覺複雜度降低多少
3. 原樣輸出,這就需要記錄去除特殊符號後的串對應原串相應位置.(很常用的方法)
import java.util.Scanner;
public class Test {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
int max = 0 ,x = 0,y = 0;//分別記錄最大值,和對應原位置的下標
String str = input.nextLine();
int[] p = new int[str.length()];//記錄原位置的陣列
char[] s = new char[str.length()];//記錄去除特舒符號後的串
//去除特殊符號並記錄新陣列中元素在原陣列中位置
for (int i = 0,m = 0; i < str.length(); i++) {
if (Character.isLetter(str.charAt(i))) {
p[m] = i;
s[m++] = Character.toLowerCase(str .charAt(i));
}
}
//開始查找回文串,利用StringBuilder的reverse來進行比較即可
//基本思路就是暴力方法
String strTemp = new String(s).trim();
for (int i = 0; i < strTemp.length(); i++) {
for (int j = i+1; j < strTemp.length(); j++) {
StringBuilder builder = new StringBuilder(strTemp.substring(i, j));
//當兩者正反一致的時候說明為迴文串
if (builder.toString().equals(builder.reverse().toString())) {
if (j-i > max) {
max = j - i;
x = i;
y = j;
}
}
}
}
System.out.println(str.substring(p[x],p[y]));
}
}
5.1.3週期串
對於週期性問題,大多數都可以通過取餘來得到一定的週期關係.對於定長字串最好的操作還是轉換成字元陣列,方便修改和查詢.
import java.util.Arrays;
public class Test2 {
public static void main(String[] args) {
String str = "aaa";
char[] c = (str).toCharArray();
//i的值實際上就是迴圈節的大小,故要從1開始,還可以避免除0
for (int i = 1; i < c.length; i++) {
boolean iscan = true;//標誌變數
if ((c.length) % i == 0) {//如果能整除i,則表示可能為當前這個子串
for (int j = i; j < c.length; j++) {
//因為是迴圈,這裡利用取餘來判斷
if (c[j] != c[j%i]) {
iscan = false;
break;
}
}
if (iscan) {
System.out.println(i);
}
}
}
System.out.println(Arrays.toString(c));
}
}
5.31 6174問題
傳統的學習迴圈,學習怎麼取出數字的指定位數
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.Scanner;
public class Test3 {
public static void main(String[] args) {
//利用hash連結串列來儲存每次計算產生的結果
LinkedHashMap<Integer, Integer> map = new LinkedHashMap<>();
Scanner input = new Scanner(System.in);
int n = input.nextInt();
map.put(n, n);
int count = 0;
//迴圈小於1000,也就是最多找1000個整數
while(count <= 1000){
int max = getNum(n, true);
int min = getNum(n, false);
n = max - min;
//如果計算過了則退出
if (map.containsKey(n)) {
break;
}
map.put(n, n);
count++;
}
System.out.println(map.toString()+"---"+n);
}
/**
* 輸入一個整數,計算出他正排和倒排的值
* @param n
* @param order true表示正排
* @return
*/
private static int getNum(int n,boolean order){
int[] arr = new int[4];
int i = 0;
while(n>0){
arr[i] = n % 10;
n = n / 10;
i++;
}
Arrays.sort(arr);
if (order) {
return arr[3]*1000+arr[2]*100+arr[1]*10+arr[0];
}
return arr[0]*1000+arr[1]*100+arr[2]*10+arr[3];
}
}
5.31 字母重排
這裡涉及了字串的比較,一位一位的比較太過於繁瑣,所以可以先排序後比較,就方便很多了
5.41 Cantor數表
這種找規律得題一般可以用技巧,先用預處理把一些可能用到計算的資料先儲存起來,後面直接用,另外注意java裡面寫好的二分查詢方法,很實用,還能自定義比較器
import java.util.Arrays;
import java.util.Scanner;
public class Test4 {
public static void main(String[] args) {
//預處理計算出前K項的和
int arr[] = new int[1000];
for (int i = 1; i < arr.length; i++) {
arr[i] = (i+1)*i/2;
}
System.out.println(Arrays.toString(arr));
//主程式開始
Scanner input = new Scanner(System.in);
// int n = input.nextInt();
int n = 7;
//二分查詢,找到比這個數小的位置的k
int mid = Arrays.binarySearch(arr, n);
if (mid<0) {//如果沒找到則返回插入位置,也就是第一個小於n得位置
mid = -mid-1;//確定該元素是第mid+1斜線上的元素
System.out.println((mid+1+n-arr[mid])+"/"+(arr[mid]-n+1));
}else {//如果找到了則直接輸出
System.out.println(mid+"/"+(n-arr[mid]+1));
}
}
}
6.1.1卡片遊戲
主要是java佇列得簡單用法,程式碼很清晰,另外這是雙端佇列,可以直接操作隊頭或者隊尾
import java.util.ArrayDeque;
public class Test5 {
public static void main(String[] args) {
//構造一個初始容量為10得佇列
ArrayDeque<Integer> queue = new ArrayDeque<>(10);
//對於佇列一般實用offer,poll,peek等方法,這幾個可以通過返回值判斷是否成功
for (int i = 1; i < 8; i++) {
queue.offer(i);
}
while(!queue.isEmpty()){
System.out.println(queue.pollFirst());;
if (queue.peek() != null) {
queue.offer(queue.poll());
}
}
}
}
6.3.1小球下落
對於二叉樹得陣列表示法,因為知道了深度就知道二叉樹總節點數,因為二叉樹本身是等比數列,可以用1<<D
來表示其節點數,而且如果按從上到下從左到右排序得話,節點k得左孩子是2k,右孩子是2k+1
import java.util.Arrays;
import java.util.Scanner;
public class Test6 {
public static void main(String[] args) {
int D,I;
Scanner input = new Scanner(System.in);
D = input.nextInt();
I = input.nextInt();
boolean[] s = new boolean[1<<D];//s[0]不用
for (int j = 0; j < I; j++) {//對小球迴圈
//每一個小球滾落模擬
int j2 = 1;
for (; ;) {
s[j2] = !s[j2];//轉動開關
if (s[j2]) {//關閉向左
j2 = 2*j2;
}else {//開啟向右
j2 = 2*j2+1;
}
if (j2 > s.length-1) {
break;
}
}
System.out.println(j2/2);//除以2找到最後經過的節點
}
System.out.println(Arrays.toString(s));
}
}
6.4最大公約數問題
最大公約數最方便的就是輾轉相除法,最小公倍數就是a*b/gcd(a,b)
public static int gcd(int a, int b) {
//基本公式如果a%b!=0 那麼 gcd(a,b) = gcd(b,a%b)
while (true) {
if ((a = a % b) == 0)
return b;
if ((b = b % a) == 0)
return a;
}
}