LeetCode practice

子集和问题:给定一组数和一个值,从这组数中选出若干个数使其和为给定的值。这是个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<n;i++)
 5   {
 6     if(b>0) b+=a[i];
 7     else b=a[i];
 8     if(sum<b) sum=b;
 9   }
10   return sum;
11 }
View Code

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<j时),其中i≤t≤j-1

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

 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<nums.length;i++)
 7         {
 8             tmp=b1+nums[i];
 9             b1=b2;
10             if(b2<tmp)
11             {
12                 b2=tmp;
13             }
14         }
15         return b2;
16     }
View Code

对于第一、第三种,由于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时

 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     }
View Code

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 }

 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     }
View Code

5.3、最长回文子序列:https://leetcode.com/problems/longest-palindromic-subsequence/#/description

法1:即求字符串S与逆串的最长公共子序列

 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<c.length;i++)
 7         {
 8             c[0][i]=0;
 9             c[i][0]=0;
10         }
11         
12         for(int i=1;i<=len;i++)
13         {
14             for(int j=1;j<=len;j++)
15             {
16                 if(s.charAt(i-1)==s.charAt(len-j))
17                 {
18                     c[i][j]=c[i-1][j-1]+1;
19                 }
20                 else
21                 {
22                     c[i][j] = c[i-1][j]>c[i][j-1] ? c[i-1][j] : c[i][j-1];
23                 }
24             }
25         }
26         return c[len][len];
27     }
View Code

法2:动态规划

设字符串为s,f(i,j)表示s[i..j]的最长回文子序列。 最长回文子序列长度为f(0, s.length()-1)
状态转移方程如下:
当i>j时,f(i,j)=0。
当i=j时,f(i,j)=1。
当i<j并且s[i]=s[j]时,f(i,j)=f(i+1,j-1)+2。
当i<j并且s[i]≠s[j]时,f(i,j)=max( f(i,j-1), f(i+1,j) )。
注:如果i+1=j并且s[i]=s[j]时,f(i,j)=f(i+1,j-1)+2=f(j,j-1)+2=2,这就是当i>j时f(i,j)=0的好处。

 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<len;j++)//由于递推式每次最多前移一列,因此从第一列起。但由于创建f时元素自动初始化为0,所以这里可以从i起
10             for(int j=i;j<len;j++)
11             {
12                 if(i>j) f[i][j]=0;
13                 else if(i==j) f[i][j]=1;
14                 else
15                 {//i<j
16                     if(s.charAt(i)==s.charAt(j)) f[i][j]=f[i+1][j-1]+2;
17                     else f[i][j]=Math.max(f[i+1][j],f[i][j-1]);
18                 }
19             }
20         }
21         return f[0][len-1];
22     }
View Code

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]
//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<len;j++)
15             {
16                 dp[i][j]= (j==i+1)?(s.charAt(i)==s.charAt(j)):(dp[i+1][j-1] && (s.charAt(i)==s.charAt(j)));
17                 if((dp[i][j]==true) && (resJ-resI+1 < j-i+1))
18                 {
19                     resI=i;
20                     resJ=j;
21                 }
22             }
23         }
24         return s.substring(resI,resJ+1);
25     }
26 }
View Code

 

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的个数

 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 }
View Code

法2:Z = N/5 + N /(5*5) + N/(5*5*5),直到式子为0

 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 }

6、给定n、m,求使得 i*j 为完全平方数的序列 (i,j) 的个数,其中 i ∈[1,n]、j ∈[1,m]:

 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     }
View Code

7、给定一个由数字组成的字符串,求出其可能回复的所有IP地址。如"25525512110"对应的ip地址可以为[255,255,121,10, 255,255,12,110]

 1     private static void split(long sVal, int[] segments, int segmentId, List<String> ips) {// split(Long.parseLong(str), new int[4], 3, ipStrs);
 2         if (segmentId == 0) {
 3             if (0 <= sVal && sVal <= 255) {
 4                 ips.add(sVal + "," + segments[1] + "," + segments[2] + "," + segments[3]);
 5             }
 6         } else {
 7             int mod, segmentVal;
 8             for (int exp = 1; exp <= 3; exp++) {
 9                 mod = (int) Math.pow(10, exp);
10                 segmentVal = (int) (sVal % mod);
11                 if (0 <= segmentVal && segmentVal <= 255) {
12                     segments[segmentId] = segmentVal;
13                     split(sVal / mod, segments, segmentId - 1, ips);
14                 }
15             }
16         }
17     }
View Code

8、给定一个随机生成器,生成0和1的概率分别为0.5,如何构造生成0和1的概率分别为0.3、0.7的随机生成器?

法:对0、1进行组合。

 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 }

随机生成器生成0和1的概率分别为p和1-p,如何构造等概率随机生成0和1的生成器?

法:由于生成01和10的概率均为p(1-p),所以可以根据之实现:

 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 }

9、一个递减序列循环左移若干位后,从其中查找一个数的O(lgn)方法:类似于折半查找

 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]) ||(v>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 }

 

其他(quiz)

蛇形打印(打印对角行元素,只不过每次右上、左下方向依次交换)方阵:

 1 #include <stdio.h>
 2 
 3 #define M 100
 4 
 5 void traverse(int a[][M],int n)
 6 {//每次打印的数据个数为1、2、3、...、n、n-1、...、2、1,依次打印之,只不过右上方向和左下方向时坐标相应地增减下 
 7     int i=0,j=0;
 8     int count,loop;
 9     int dir=1;
10     for(count=1;count<=n;count++)
11     {
12         if(dir==1)
13         {//右上方向 
14             for(loop=1;loop<count;loop++)
15             {
16                 printf("%d ",a[i--][j++]);
17             }
18             printf("%d ",a[i][j]);
19             if(j==n-1) i++;
20             else j++;
21         }
22         else
23         {//左下方向 
24             for(loop=1;loop<count;loop++)
25             {
26                 printf("%d ",a[i++][j--]);
27             }
28             printf("%d ",a[i][j]);
29             if(i==n-1) j++;
30             else i++;
31         }
32         dir=-dir;
33     }
34     
35     for(count=n-1;count>0;count--)
36     {
37         if(dir==1)
38         {
39             for(loop=1;loop<count;loop++)
40             {
41                 printf("%d ",a[i--][j++]);
42             }
43             printf("%d ",a[i][j]);
44             if(j==n-1) i++;
45             else j++;
46         }
47         else
48         {
49             for(loop=1;loop<count;loop++)
50             {
51                 printf("%d ",a[i++][j--]);
52             }
53             printf("%d ",a[i][j]);
54             if(i==n-1) j++;
55             else i++;
56         }
57         dir=-dir;
58     }
59 }
60 
61 int main(int argc,char *argv[])
62 {
63     int n=4;
64     int a[n][M];
65     for(int i=0;i<n;i++)
66     {
67         for(int j=0;j<n;j++)
68         {
69             a[i][j]=n*i+j+1;
70         }
71     }
72     traverse(a,n);
73 }
View Code

 

转载于:https://www.cnblogs.com/z-sm/p/6589803.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值