leetcode 135 candy 97 Interleaving String 316 Remove Duplicate Letters

135 candy

题目大意是有n个孩子,每个孩子有一个rating值,现在需要给这n个孩子发糖果,要求如下:

  • 每个孩子至少得到一个糖果
  • 对于相邻的孩子的rating值高的应该得到更多的糖果

问发得最少糖果是多少?


解题思路是,对入任意一个孩子,如果他比左边连续p个孩子都高,比右边连续q个孩子都高,那么他至少得到min(p,q)+1个糖果,才能瞒住题目中的条件。
所以可以声明两个数组:

left[i]:记录从左边开始并以第i个孩子为结尾的严格递增的个数
right[i]:记录从右边开始并以第i个孩子为结尾的严格递增的个数

求得他们的时间复杂度为O(n),最后在一次计算一次每个孩子得到的最少糖果,时间复杂度为O(n).


int max(int a, int b){
    return a>b?a:b;
}
int candy(int* ratings, int ratingsSize) {
    int left[ratingsSize], right[ratingsSize];
    left[0] = 1;
    for(int i=1; i<ratingsSize; i++){
        if(ratings[i] > ratings[i-1]){
            left[i] = left[i-1] + 1;
        }
        else{
            left[i] = 1;
        }
    }
    int ans = 0;
    right[ratingsSize-1] = 1;
    ans += max(left[ratingsSize-1], right[ratingsSize-1]);
    for(int i=ratingsSize-2; i>=0; i--){
        if(ratings[i] > ratings[i+1]){
            right[i] = right[i+1] +1;
        }
        else{
            right[i] = 1;
        }
        ans += max(right[i], left[i]);
    }
    return ans;
}

97 Interleaving String

题目大意是给出三个字符串s1,s2,s3,问是否可以有s1和s2交替组成s3。
这里的交替组成表示依次保持s1和s2中的字符的顺序,每次从s1和s2中取出若干字符组成s3即可,当然每个字符只能使用一次


使用动态规划解决这个问题:

dp[i][j]表示第一个字符串的前i个字符和第二个字符串的前j个字符,是否可以构成s3的前i+j个字符
递推公式如下:
dp[i][j] = (s1[i] == s3[i+j+1] && dp[i-1][j]) || (s2[j] == s3[i][j-1] && dp[i][j-1]) 

时间复杂度为o(s1len * s2len)


bool isInterleave(char* s1, char* s2, char* s3) {
    int len1 = strlen(s1);
    int len2 = strlen(s2);
    int len3 = strlen(s3);
    if(len1 + len2 != len3) return false;
    bool dp[len1+1][len2+1];
    dp[0][0] = true;
    //初始化
    for(int i=0; i<len1; i++){
        if(s1[i] == s3[i]) dp[i+1][0] = dp[i][0];
        else dp[i+1][0] = 0;
    }
    for(int i=0; i<len2; i++){
        if(s2[i] == s3[i]) dp[0][i+1] = dp[0][i];
        else dp[0][i+1] = 0;
    }
    for(int i=0; i<len1; i++){
        for(int j=0; j<len2; j++){
            dp[i+1][j+1] = false;
            if(s2[j] == s3[i+j+1]) dp[i+1][j+1] = dp[i+1][j];
            //注意,需要判断前面是否已经满足,满足就不要再进行查找了,不然会出现问题
            if(s1[i] == s3[i+j+1] && !dp[i+1][j+1]) dp[i+1][j+1] = dp[i][j+1];
        }
    }
    return dp[len1][len2];
}

316 Remove Duplicate Letters

题目大意是给你一个字符串(只包含小写字母),去掉重复的字符,处理后的字符串中的字符尽可能的按照字典序输出,但是需要保证原有的先后顺序,比如给出bca,得到字符串也就是bca,不能是abc。

思路一

假设给出字符串为s,长度为len,首先处理一遍字符串,得到所有出现的字符,然后将其按照字典序保存下来,记作ss,声明用来记录最终答案的字符串ret。
接下来按照一下步骤扩充ret:
1. 声明寻找字符区间[0,len]
2. 按照字典序取出一个存在与ss中的字符但没存在ret中的字符
3. 在处理区间中找到第一次出现的位置,并对其进行统计后面出现的字符满足一下条件的个数:存在ss中但不存在ret中(不包含自身)
4. 如果统计的个数为len(ss)- len(ret)-1,则将其加入到ret中,并跳转至2
5. 如果统计的个数不为len(ss)- len(ret)-1,取出下一个存在与ss中的字符但没存在ret中的字符,跳转至3
6. 直到ss中的字符和ret中的字符相同位置

时间复杂度为O(k*k*n),其中k为返回字符串的长度小于26.

char* removeDuplicateLetters(char* s) {
    int vis[256];
    int re[256][256];
    for(int i=0; i<256; i++) vis[i] = 0;
    int len = strlen(s);
    int retlen = 0;
    for(int i=0; i<len; i++){
        if(!vis[s[i]]){
            vis[s[i]] = 1;
            retlen ++;
        }
    }
    char ss[retlen];
    int tmp = 0;
    for(int i='a'; i<='z'; i++){
        if(vis[i]){
            ss[tmp++] = i;
        }
    }
    for(int i=0; i<retlen; i++){
        vis[i] = 0;
    }
    int visch[256];
    for(int i=0; i<256; i++){
        visch[i] = 0;
    }
    tmp = 0;
    char *ret = malloc(sizeof(char)*(retlen+1));
    int now = 0;
    while(tmp < retlen){
        int rem = retlen - tmp - 1;
        //printf("%d %d\n", now, rem);
        int i = 0;
        for(; i<retlen; i++){
            if(vis[i]) continue;
            //printf("%d %c:", i, ss[i]);
            int cnt = 0;
            int j = now;
            for(; j<len; j++){
                if(s[j] == ss[i]) break;
            }
            //printf("j-%d\n",j);
            j++;
            int next = j;
            int v[256];
            for(int k=0; k<256; k++){
                v[k] = 0;
            }
            for(; j<len; j++){
               // printf("pre:j:%d s[j]:%c ss[i]:%c visch[s[j]]:%d v[s[j]]:%d\n", j, s[j], ss[i], visch[s[j]], v[s[j]]);
                if(s[j] == ss[i] || visch[s[j]]) continue;
                if(!v[s[j]]){
                    cnt++;
                    v[s[j]] = 1;
                }
               // printf("aft:j:%d s[j]:%c ss[i]:%c visch[s[j]]:%d v[s[j]]:%d\n", j, s[j], ss[i], visch[s[j]], v[s[j]]);    
            }
            //printf("cnt-%d\n",cnt);
            if(cnt == rem) {
                now = next;
                break;
            }     
        }
       // printf("update:%d %c ----------------->\n", i, ss[i]);
        ret[tmp++] = ss[i];
        visch[ss[i]] = 1;
        vis[i] = 1;
    }
    ret[tmp] = 0;
    return ret;
}
思路二

对于字符串的每个位置,维护一个cnt值,表示其后面出现不同字符的个数,每次选出
- cnt值最大的
- 字典序最小的
- 下标最小的
作为返回的下一个字符,并一次从上次查找到字符的地方开始找下一个字符
时间复杂度为O(k*n)

char* removeDuplicateLetters(char* s) {
    int vis[27],v[27];//vis for the ret and v for the current
    for(int i=0; i<27; i++){
        vis[i] = 0;
        v[i] = 0;
    }
    int retsize = 0;
    char *ret = malloc(sizeof(char));
    int now = 0;
    int len = strlen(s);
    while(now < len ){
        int cnt = 0;
        for(int i=0; i<27; i++){
            v[i] = 0;
        }
        int next = len-1;
        for(int i=len-1; i>=now; i--){
            //忽略已经拥有的字母
            if(vis[s[i]-'a']) continue;
            //统计出现的频率
            if(!v[s[i]-'a']){
                v[s[i]-'a'] = 1;
                cnt++;
                next = i;
            }
            else{
                if(s[i] <= s[next]) next = i;
            }
        }
        if(cnt == 0) break;
        retsize ++;
        ret = realloc(ret, sizeof(char)*retsize);
        ret[retsize-1] = s[next];
        vis[s[next]-'a'] = 1; 
        now = next+1;
    }
    ret = realloc(ret, sizeof(char)*(retsize+1));
    ret[retsize] = 0;
    return ret;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值