子集和问题:给定一组数和一个值,从这组数中选出若干个数使其和为给定的值。这是个NPC问题。
1、https://leetcode.com/problems/counting-bits/#/solutions
给定一非负Integer num,求[0,num]每个数的二进制形式中1的个数 f[num+1]。
解法:可用最朴素的方法逐个求,但其实有规律: f[i] = f[i / 2] + i % 2 或 f[i] = f[i&(i-1)] + 1;
2、Single Number:一组Integer类型的数,除了一个出现N次外,其他都出现M次,找出此数(N ≠ M)。(相关题目:https://leetcode.com/problems/single-number-ii/#/description)
法1:排序,时间复杂度最少O(nlgn)
法2:利用哈希表计每个数出现此时,时间复杂度近似O(n),空间复杂度为O(n)
法3:(推荐)思路:每个Integer32位,对于每一位,各个数该位上的值的和对M求模,结果即为特殊的数在该位上的和的N倍(若N>M则为 N%M倍)。时间复杂度为O(n),空间复杂度为O(1)
代码如下:
1 public int singleNumber(int[] nums, int M, int N) {// with constant memory 2 int res = 0; 3 int tmpCurBitSum = 0; 4 for (int bit = 0; bit < 32; bit++) { 5 tmpCurBitSum = 0; 6 for (int i : nums) { 7 tmpCurBitSum += ((1 << bit) & i) >>> bit; 8 } 9 res |= ((tmpCurBitSum % M)/(N%M)) << bit; 10 } 11 return res; 12 }
特殊情况:M为偶数,N为奇数,如当M=2,N=1时候即为经典的题“除了一个数出现一次外其他数都出现2次”,此时还可以用更简单的方法:所有数异或,结果即为特殊数。
题目变换:有两个分别出现奇数次,其他的都出现偶数次。仍可以用法1或法2解决,但空间复杂度高;另法:所有数异或得到特殊的那两个数的异或值a,对于a中值为1的位,在原两数中该位上的值肯定不同,故可选一位将所有数分成两组,两特殊数分别在两组中,两组各自异或得到两个数即为结果。(相关题目:https://leetcode.com/problems/single-number-iii/#/solutions)
3、String to Integer(atoi)
逐个字符处理,res=res*10+ ch-'0';,但是需要判断溢出,细节处理有点麻烦。可以换个思路判断:res> (Integer.MAX_VALUE-(ch-'0'))/10 时即溢出,这样可以省很多细节。
相关题目:https://leetcode.com/problems/string-to-integer-atoi/#/description
4、子段和问题:(递推关系设计:连续子串——包含边界元素时,子序列——不要求包含边界元素)
4.1、字段和的最大值
设有序列 a[1,2,...,n]
1、最大子段和(一个子段):设 b[j] 为 a[1,2,...,j] 包含 a[j] 的最大子段和,则根据b[j-1]是否大于0有: b[j] = max{a[j], b[j-1]+a[j] }


1 int maxSum(int n,int *a) 2 { 3 int sum=0,b=0; 4 for(int i=0;i) 5 { 6 if(b>0) b+=a[i]; 7 else b=a[i]; 8 if(sumb; 9 } 10 return sum; 11 }
2、最大m子段和(找出m个子段,可以相邻):设 b[i,j] 为 a[1,2,...,j] 前j项中i个子段的最大和且第i个子段包含a[j](1≤i≤m, i≤j≤n),则根据第i个子段是否仅仅包含a[j]有:
b[i,j]= 0 (i=0或j=0时)
b[i-1,j-1]+a[j] (i==j时)
max{ b[i,j-1]+a[j], (max b[i-1,t])+a[j] } (i 3、最大不相邻子段和(找出任意个子段,但子段不能相邻):设 b[j] 为 a[1,2,...,j] 不相邻子段和的最大值(不要求包括a[j]),则根据 b[j] 是否包括 a[j] 有:b[j]= max{ a[j]+b[j-2], b[j-1] }。相关:LeetCode198:House Robber 对于第一、第三种,由于b[j]只和前一个或两个状态有关,因此可以不设数组b[]而是采用数个变量来求;对于第二种同理可以只用两个数组来实现。 4.2、子段和的种数 设有序列 a[1,2,...,n],给定数sum 1、求序列中两数和等于sum的种数:LeetCode1:TwoSum 2、求序列中若干个数和等于sum的种数:设dp[i,j]为从序列前i个数中选若干个使得和为j的种数,则: dp[i,j]= 1,i=0且j=0时; 0,i=0且j=1,2,...sum时; dp[i-1,j],a[i]>j时; dp[i-1,j] + dp[ i-1,j-a[i] ],a[i]≤j时 4.3、推广:最大/最小连续子段积 设有序列 a[1,2,...,n],设f(k)为a[1,2,...k]中包含a[k]元素的最大连续子数组积,相应的g(k)为包含a[k]的最小连续子数组积, 则 f(k) = max( f(k-1) * A[k], A[k], g(k-1) * A[k] ), g(k) = min( g(k-1) * A[k], A[k], f(k-1) * A[k] ) ,从而易在O(n)内求之。 5、最长公共子序列、最长公共子串、最长回文子序列、最长回文子串 5.1、最长公共子序列:设 dp[i,j] 为序列a1[1,2,...,i ]、a2[1,2,...j ] 的最长公共子序列的长度,则dp[i,j]= (a1[i]==a2[j])? (dp[i-1,j-1]+1): max{ dp[i-1,j], dp[i,j-1] } 5.2、最长公共子串:设 dp[i,j] 为序列a1[1,2,...,i ]、a2[1,2,...j ] 的包含未元素的最长公共子串的长度,则dp[i,j]= (a1[i]==a2[j])? (dp[i-1,j-1]+1): 0 } 5.3、最长回文子序列:https://leetcode.com/problems/longest-palindromic-subsequence/#/description 法1:即求字符串S与逆串的最长公共子序列 法2:动态规划 设字符串为s,f(i,j)表示s[i..j]的最长回文子序列。 最长回文子序列长度为f(0, s.length()-1) 5.4、最长回文子串:https://leetcode.com/problems/longest-palindromic-substring/#/description //设dp[i,j]表示si,...,sj是否为回文子串,则dp[i,j]= dp[i+1,j-1] && (s[i]==s[j]), i≤j; 初始:dp[i,i]=true,dp[i,i+1]=s[i]==s[i+1] 5.5、n! 中0的个数,直接求n!的值再数0的个数显然数据一大几乎不可能求得。转换方向:由于0由2*5产生(就算是4*5等产生,最终也是由2*5产生),所以n! 中0的个数="2*5"的个数=5的个数(因为一个数的因子中2的个数肯定比5的个数多) 求n! 中5的个数:由于n!=1*2*...*n,(详情参阅:n的阶乘末尾0的个数) 法1:穷举法,求1~n中每个数的因子5的个数 法2:Z = N/5 + N /(5*5) + N/(5*5*5),直到式子为0 6、给定n、m,求使得 i*j 为完全平方数的序列 (i,j) 的个数,其中 i ∈[1,n]、j ∈[1,m]: 7、给定一个由数字组成的字符串,求出其可能回复的所有IP地址。如"25525512110"对应的ip地址可以为[255,255,121,10, 255,255,12,110] 8、给定一个随机生成器,生成0和1的概率分别为0.5,如何构造生成0和1的概率分别为0.3、0.7的随机生成器? 法:对0、1进行组合。 随机生成器生成0和1的概率分别为p和1-p,如何构造等概率随机生成0和1的生成器? 法:由于生成01和10的概率均为p(1-p),所以可以根据之实现: 9、一个递减序列循环左移若干位后,从其中查找一个数的O(lgn)方法:类似于折半查找 其他(quiz) 蛇形打印(打印对角行元素,只不过每次右上、左下方向依次交换)方阵:
1 public int maxNonadjacentSun(int[] nums) {
2 if(nums.length==0)return 0;
3 if(nums.length==1)return nums[0];
4
5 int b1=0,b2=nums[0],tmp;
6 for(int i=1;i
1 public static int resolve(int[] a, int sum) {// num of solutions that addup to sum
2 if (a == null || a.length == 0) {
3 return 0;
4 }
5 int n = a.length;
6 int[][] dp = new int[n + 1][sum + 1];
7 dp[0][0] = 1;
8 // dp[i][k]=0,k>0,默认被初始化了
9 for (int i = 1; i <= n; i++) {
10 for (int j = 0; j <= sum; j++) {
11 if (a[i - 1] > j) {
12 dp[i][j] = dp[i - 1][j];
13 } else {
14 dp[i][j] = dp[i - 1][j] + dp[i - 1][j - a[i - 1]];
15 }
16 System.out.printf("(%d,%d):%d\n", i, j, dp[i][j]);
17 }
18 }
19 return dp[n][sum];
20 }
1 private static int resolve(String s1, String s2) {
2 if (s1 == null || s2 == null) {
3 return 0;
4 }
5 int n = s1.length();
6 int m = s2.length();
7 int max = 0;
8 int[][] dp = new int[n + 1][m + 1];
9 for (int i = 1; i <= n; i++) {
10 for (int j = 1; j <= m; j++) {
11 dp[i][j] = (s1.charAt(i - 1) == s2.charAt(j - 1)) ? (dp[i - 1][j - 1] + 1) : 0;
12 if (max < dp[i][j]) {
13 max = dp[i][j];
14 }
15 }
16 }
17 return max;
18 }
1 public int longestPalindromeSubseq1(String s) {
2 if(s==null || s.length()==0) return 0;
3
4 int len=s.length();
5 int [][]c=new int[len+1][len+1];
6 for(int i=0;i
状态转移方程如下:
当i>j时,f(i,j)=0。
当i=j时,f(i,j)=1。
当i
1 public int longestPalindromeSubseq(String s) {
2 if(s==null || s.length()==0) return 0;
3
4 int len=s.length();
5 int [][]f=new int[len][len];
6
7 for(int i=len-1;i>=0;i--)//由于递推式每次最多后移一行,因此从最后一行起
8 {
9 // for(int j=0;j
//O(n2),O(n2)
1 public class Solution {
2 //最长回文子串和最长回文子序列不一样。。
3 //设dp[i,j]表示si,...,sj是否为回文子串,则dp[i,j]= dp[i+1,j-1] && (s[i]==s[j]), i≤j; 初始:dp[i,i]=true,dp[i,i+1]=s[i]==s[i+1]
4 //O(n2),O(n2)
5 public String longestPalindrome(String s) {
6 if(s==null || s.length()==0) return "";
7 int len=s.length();
8
9 boolean [][]dp=new boolean[len][len];//标记dp[i+1,j-1]即左下角是否为回文子串
10 int resI=0,resJ=0;
11 for(int i=len-1;i>=0;i--)
12 {
13 dp[i][i]=true;//dp[i,i]=true;
14 for(int j=i+1;j
1 int fun1(int n)
2 {
3 int num = 0;
4 int i,j;
5 for (i = 5;i <= n;i += 5)
6 {
7 j = i;
8 while (j % 5 == 0)
9 {
10 num++;
11 j /= 5;
12 }
13 }
14 return num;
15 }
1 int fun2(int n)
2 {
3 int num = 0;
4
5 while(n)
6 {
7 num += n / 5;
8 n = n / 5;
9 }
10
11 return num;
12 }
1 public static void main(String[] args) {
2 Scanner sc = new Scanner(System.in);
3 int res = 0;
4 int n = sc.nextInt();
5 int m = sc.nextInt();
6 // ssr(a,b)是整数 等价于sqrt(a*b)是整数 等价于a*b是完全平方数
7 // 暴力O(n*m)在大数据时超时,以下为O(n*sqrt(m))的方法
8 for (int i = 1; i <= n; i++) {
9 // 找到能整除i的最大的完全平方数s
10 int s = 1;
11 for (int x = 2; x * x <= i; x++) {
12 if (i % (x * x) == 0) {
13 s = x * x;
14 }
15 }
16 int r = i / s;// n去掉s因子后的结果
17 // 要使a*b是完全平方数,b需要因子r和一个完全平方数
18 for (int y = 1; y * y * r <= m; y++) {
19 res++;
20 }
21 }
22 System.out.println(res);
23 sc.close();
24 }
1 private static void split(long sVal, int[] segments, int segmentId, List
1 int MyFun()
2 {
3 int n1=fun();
4 int n2=fun();
5 int n3=fun();
6 int n4=fun();
7 int n=n1;
8 n|=n2<<1;
9 n|=n3<<2;
10 n|=n4<<3;
11 if(n<=2) return 0;
12 else if(n<10) return 1;
13 else return MyFun();
14 }
1 int MyFun()
2 {
3 int n1=fun();
4 int n2=fun();
5 int n=n1();
6 n|=n2<<1;
7 if(n==2) return 0;
8 else if(n==1) return 1;
9 else return MyFun();
10 }
1 int find(int data[],int n,int v)
2 {
3 int s=0,e=n-1,mid;
4 while(s<=e)
5 {
6 mid=s+(e-s)/2;
7 if(v==data[mid]) return mid;
8 else if( (data[s]>=v && v>data[mid]) || (v<=data[s] && data[s]data[mid] && data[s]<data[mid]) )
9 {//有三种情况使得落于左半子序列:左半子序列递减且v位于其间、左半子序列非递减且v位于非递减的前段如21765、左半子序列非递减且v位于非递减的后段如21765、
10 e=mid-1;
11 }
12 else//落于右半子序列的情况类似
13 {
14 s=mid+1;
15 }
16 }
17 return -1;
18 }
1 #include