终于还是开学了,还是坚持每日一t的节奏,只是更新可能不是很及时了
214.最短回文串
暴力的算法就是进行枚举,找到从头开始最长的一个回文字符串。然后反转后缀添加上去。
这里主要注意一下语言:
s.substring(begin, end)
截取字符串new StringBuilder(s.substring(sh)).reverse().append(s).toString();
熟练使用StringBuffer
class Solution {
public String shortestPalindrome(String s) {
int sh = 0;
for (int i = s.length(); i>0; i--){
String sub = s.substring(0,i);
if (isPali(sub)) {
sh = i;
break;
}
}
return new StringBuilder(s.substring(sh)).reverse().append(s).toString();
}
public boolean isPali(String s){
int n = s.length();
int l = 0, r = n-1;
while (r>=l){
if (s.charAt(l) != s.charAt(r)) return false;
l++;
r--;
}
return true;
}
}
但是这种方法的复杂度太高,还可以采用字符串哈希的方法。这种方法第一次用到,比较神奇。基本的思路是把每个字符看成一个base
进制的,然后从前往后遍历,如果正序和逆序一样,等价于两个数字一样。这里需要注意,由于进制太大,整型无法存放,因此我们取模。
一般来说,我们选取一个大于字符集大小(即字符串中可能出现的字符种类的数目)的质数作为 base
,再选取一个在字符串长度平方级别左右的质数作为mod
,产生哈希碰撞的概率就会很低。
class Solution {
public String shortestPalindrome(String s) {
int base = 131, mod = 1000000007;
int l = 0, r = 0;
int mul = 1, best = -1;
for (int i = 0; i < s.length(); i++){
l = (int) (((long) l * base + s.charAt(i)) % mod);
r = (int) ((r + (long) mul * s.charAt(i)) % mod);
if (l == r) best = i; // best存储了最后依次相等的位置,因此在反转时候需要从best+1开始
mul = (int) ((long) mul * base % mod);
}
return new StringBuffer(s.substring(best+1)).reverse().append(s).toString();
}
}
对于字符串哈希还有一个典型题目字符串哈希
332.重新安排行程
很明显是一道图论的题目,有两个特点,一个是不重复的经过每一条路线,另外一个是按照字母序的方法。
对于第一个特点,其实涉及到一个我不是很熟悉的知识点,欧拉图。
对于欧拉图的题目我们一般采用的算法就是Hierholzer算法。基本思路是从起点开始依次进行dfs搜索,每次途径一条边时都要删除,将没有任何邻居的孤立点加入栈中。最后将整个栈反转即可。
class Solution {
private Map<String, PriorityQueue<String>> map;
private List<String> ans;
public List<String> findItinerary(List<List<String>> tickets) {
map = new HashMap<>();
ans = new LinkedList<>();
for (List<String> cur:tickets){
String fro = cur.get(0), to = cur.get(1);
if (!map.containsKey(fro)) map.put(fro, new PriorityQueue<String>());
map.get(fro).add(to);
}
dfs("JFK");
Collections.reverse(ans);
return ans;
}
public void dfs(String cur){
//双重判断,存在这个key并且时孤立节点
while (map.containsKey(cur) && map.get(cur).size()>0){
String next = map.get(cur).poll();
dfs(next);
}
ans.add(cur);
}
}
491.递增子序列
也是一道可以考虑到字符串哈希的题目。首先因为要枚举子序列,我们考虑到使用未的方法,然后我们为了去重不同的序列,对每一个序列进行哈希,并且判断每一个数列是不是递增的。
class Solution {
// 这里的ArrayList需要声明内部的空间
List<List<Integer>> ans = new ArrayList<List<Integer>>();
Set<Integer> set = new HashSet<>();
List<Integer> temp = new ArrayList<>();
int n;
public List<List<Integer>> findSubsequences(int[] nums) {
n = nums.length;
for (int i = 1 ; i<(1<<n); i++){
newnum(i, nums);
int code = gethash();
if (check() && !set.contains(code)){
ans.add(new ArrayList<Integer>(temp));
set.add(code);
}
}
return ans;
}
public void newnum(int num, int[] nums){
temp.clear();
for (int i = 0; (num>>i)>0; i++){
if (((num>>i)&1) == 1) {
temp.add(nums[i]);
}
}
}
public boolean check(){
int m = temp.size();
for (int i = 1; i<m;i++){
if (temp.get(i)<temp.get(i-1))return false;
}
return temp.size() >= 2;
}
public int gethash(){
base的选择需要大于总的可能性
int mod = 1000000007, base = 263;
int left = 0;
for (int i = 0; i<temp.size();i++){
因为范围是-100,100,所以我们能都给平移到正的范围
left = (int)((temp.get(i)+101+(long)left*base)%mod);
}
return left;
}
}
679.24点游戏
主要思想还是进行回溯,依次枚举每两个数字之间可能的操作。
class Solution {
static final int TARGET = 24;
static final double EPSILON = 1e-6;
static final int ADD = 0, MULTIPLY = 1, SUBTRACT = 2, DIVIDE = 3;
public boolean judgePoint24(int[] nums) {
List<Double> list = new ArrayList<>();
for (int i = 0; i<nums.length;i++) list.add((double)nums[i]);
return solve(list);
}
public boolean solve(List<Double> list){
if (list.size() == 1) return (Math.abs(list.get(0) - TARGET)<EPSILON);
if (list.size() == 0) return false;
int n = list.size();
for(int i = 0; i<n; i++){
for (int j = 0 ;j<n;j++){
if (i!=j){
List<Double> list2 = new ArrayList<>();
for (int k = 0; k<n;k++){
if (k!=i && k!=j) list2.add(list.get(k));
}
for (int k = 0; k<4;k++){
if (i>j && k<2) continue;
if (k == 0) list2.add(list.get(i)+list.get(j));
else if (k == 1) list2.add(list.get(i)*list.get(j));
else if (k == 2) list2.add(list.get(i)-list.get(j));
else if (k == 3) {
list2.add(list.get(i)/list.get(j));
}
// 回溯的关键
if (solve(list2)) return true;
list2.remove(list2.size()-1);
}
}
}
}
return false;
}
}