1. 程式人生 > >演算法競賽入門讀書筆記(自用,慎入)

演算法競賽入門讀書筆記(自用,慎入)

例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;
        }
    }