这两天一直在刷PAT的程序设计题,在基础题中出现最多的就是HASH和穷竭 搜索了 。今天先来整理下穷竭搜索吧。
穷竭搜索是将所有的可能性罗列出来,在其中找到答案的方法,我们也称为暴力法或者蛮力法,有时候对算法的时间和空间要求不高时,可以使用这个办法将所有的结果枚举出来,在结果集中寻找到我们想要的答案。
一般我们通过DFS或者BFS进行实现,也可以直接使用迭代枚举出结果,直接上例子。
/*
Given a string S, find the longest palindromic substring in S.
You may assume that the maximum length of S is 1000,
and there exists one unique longest palindromic substring.
Example
Given the string = "abcdzdcab", return "cdzdc".
Challenge
O(n2) time is acceptable. Can you do it in O(n) time.
大概意思是求最长的回文子串
*/
/*
* 解法:
* 使用穷竭搜索,用一个变量记录最长的回文子串
* 找到更长的子串后更新
*/
public class Main3 {
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
String line = scan.nextLine();
// 最长回文子串长度
int longest = 0;
// 左边索引
int left = 0;
// 右边索引
int right = 0;
for (int i = 0; i < line.length(); i++) {
for (int j = i + 1; j < line.length() + 1; j++) {
String subStr = line.substring(i, j);
if (isPalinDromic(subStr) && j - i > longest) {
longest = j - i;
left = i;
right = j;
}
}
}
String longestSub = line.substring(left, right);
System.out.println(longestSub);
scan.close();
}
public static boolean isPalinDromic(String str) {
if (str.isEmpty())
return false;
for (int i = 0; i < str.length(); i++) {
if (str.charAt(i) != str.charAt(str.length() - 1 - i))
return false;
}
return true;
}
}
这道题通过两层for循环枚举出所有的回文子串,在通过longest变量跟踪得到最长的 回文字符串。
再来一个DFS
/*
Implement wildcard pattern matching with support for '?' and '*'.
'?' Matches any single character.
'*' Matches any sequence of characters (including the empty sequence).
The matching should cover the entire input string (not partial).
Example
isMatch("aa","a") → false
isMatch("aa","aa") → true
isMatch("aaa","aa") → false
isMatch("aa", "*") → true
isMatch("aa", "a*") → true
isMatch("ab", "?*") → true
isMatch("aab", "c*a*b") → false
类似于实现正则匹配
*/
/*
* 解法:
* 使用DFS实现字符串匹配
*/
public class Main4 {
public static void main(String[] args) {
// Scanner scan = new Scanner(System.in);
// String line = scan.nextLine();
System.out.println(isMatch("aab", "*ba"));
// scan.close();
}
private static boolean isMatch(String s, String p) {
if (s.isEmpty() || p.isEmpty())
return false;
int si = 0;
int pj = 0;
return helper(s, si, p, pj);
}
private static boolean helper(String s, int si, String p, int pj) {
if (si == s.length() || pj == p.length()) {
if (si == s.length() && pj == p.length()) {
return true;
} else {
return false;
}
}
if (p.charAt(pj) == '*') {
while (p.charAt(pj) == '*') {
pj++;
if (pj == p.length())
return true;
}
// 对si进行递推
while (si < s.length() && !helper(s, si, p, pj)) {
si++;
}
// 如果不是子串的话,返回true
return si != s.length();
} else if (s.charAt(si) == p.charAt(pj) || p.charAt(pj) == '?') {
return helper(s, si + 1, p, pj + 1);
} else {
return false;
}
}
}
这道题类似于正则表达式的匹配问题,难点在于找到‘’*‘时的匹配字符,在找到‘*’时,必须逐渐对原字符串进行递推,知道找到下一个跟模式匹配的字符,这就是DFS的基本思路。
最后一个,经典的部分和问题
/*
* 部分和问题
*
* 给定整数a1,a2,...,an,判断是否可以从中选出若干数,使他们的和恰好为k
*
* Example input :
* n = 4
* a = {1,2,4,7}
* k = 13
* output :
* Yes {13 = 2+4+7}
*
*
* 解法思路:使用穷竭搜索(DFS)从一个状态开始,直到状态无法转移
* 此时会找到 所有的解,从此解 中寻找答案
*/
public class Main {
private static int[] A;
private static int N, K;
public static void main(String[] args) {
// input
Scanner scan = new Scanner(System.in);
N = scan.nextInt();
A = new int[N];
for (int i = 0; i < N; i++) {
A[i] = scan.nextInt();
}
K = scan.nextInt();
scan.close();
// slove and output
System.out.println(DFS(0,0));
}
/**
* 深度搜索所有可行的结果
* 已经从前n项得到了sum,对第n项之后的元素进行分支
*/
private static boolean DFS(int n, int sum) {
if(n == N ) return sum == K;
// 加上下一个元素的情况
if(DFS(n+1,sum+A[n])) return true;
// 不加下一个元素的情况
if(DFS(n+1,sum)) return true;
// 如果都没有返回,即无法等于K
return false;
}
}
DFS时会自动进行进行状态转移,我们需要对状态转移的过程了如执掌,这样才能掌握这种思想。