剑指offer字符串相关:字符串压缩+最长不含重复字符的子字符串+翻转字符串+字符串转整数

1.字符串压缩

class Solution {
public:
    string compressString(string S) {
        if(S.length()==0) return S;
        string compressResult;
        int count=0;
        int i=0;
        int j=0;
        while(i<S.length()&&j<S.length()){
            if(S[i]==S[j]){
                j++;
                count++;
            }
            else{
                compressResult+=S[i];
                std::string countnums=std::to_string(count);
                count=0;
                compressResult+=countnums;
                i=j;
            }
        }
        compressResult+=S[i];
        std::string countnums=std::to_string(count);
        compressResult+=countnums;
        if(compressResult.length()<S.length()) return compressResult;
        else return S;
    }
};

官方题解中的解法:

class Solution {
public:
    string compressString(string S) {
        if ((int)S.length() == 0) return S; // 空串处理
        string ans = "";
        int cnt = 1;
        char ch = S[0];
        for (int i = 1; i < (int)S.length(); ++i){
            if (ch == S[i]) cnt++;
            else{
                ans += ch + to_string(cnt); // 注意 cnt 要转为字符串
                ch = S[i];
                cnt = 1;
            }
        }
        ans += ch + to_string(cnt);
        return ans.length() >= S.length() ? S : ans;
    }
};

作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/compress-string-lcci/solution/zi-fu-chuan-ya-suo-by-leetcode-solution/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

2.最长不含重复字符的子字符串

力扣上的测试用例跟剑指上的不同,没有假设字符串中只包含‘a’到z的字符,会出现“  ”这样的测试用例,用动态规划dp数组的这种解法,执行效率不好。

求最大元素的方法:auto biggest=std::max_element(dp.begin(),dp.end()),返回的是一个迭代器。

std::distance(std::begin(v), smallest)  这样就把下标位置求出来了

class Solution {
public:
    int lengthOfLongestSubstring(string s) {
        if(s.empty()) return 0;
int n=s.size();
if(n<=1) return n;
//我们创建一个长度为256的数组position用来存储每个字符上次出现在字符串中位置的下标
vector<int> position(256,-1);
vector<int> dp(n,0);
dp[0]=1;
int i;
position[s[0]]=0;
for(i=1;i<n;++i){
    if(position[s[i]]==-1){
        position[s[i]]=i;//注意对位置的更新
        dp[i]=dp[i-1]+1;
    }
    else{
        int d=i-position[s[i]];
        position[s[i]]=i;
        if(d<=dp[i-1]){
            dp[i]=d;
        }
        else{
            dp[i]=dp[i-1]+1;
        }
    }
    cout<<dp[i]<<endl;
}
std::vector<int>::iterator biggest=std::max_element(std::begin(dp),std::end(dp));
return *biggest;
    }
};

基于循环的代码:执行用时显著改善

class Solution {
public:
    int lengthOfLongestSubstring(string s) {
if(s.empty()) return 0;
int n=s.size();
if(n<=1) return n;
//我们创建一个长度为256的数组position用来存储每个字符上次出现在字符串中位置的下标
vector<int> position(256,-1);
int maxLength=0;
int currentLength=0;
for(int i=0;i<n;++i){
    int preindex=position[s[i]];
    if(preindex<0||i-position[s[i]]>currentLength){
        currentLength=currentLength+1;
         if(currentLength>maxLength){
            maxLength=currentLength;
        }
    }
    else{
        if(currentLength>maxLength){
            maxLength=currentLength;
        }
        currentLength=i-position[s[i]];
    }
    position[s[i]]=i;
}
return maxLength;
    }
};

如果想要用暴力法解决的话,需要先生成所有的子串再判断这个子串中有没有重复的字符。因为n是不确定的,没有办法用循环的办法来生成子串?在网上找到了用三层循环控制来生成子串的办法。需要用回溯法生成?

3.翻转字符串

题目一:剑指offer上的解法。第一步翻转句子中所有的字符。第二步再翻转每个单词中字符的顺序。我们可以通过扫描空格来确定每个单词的起始和终止位置。

void reverse(char *pBegin,char *pEnd);
char* reverseWords(char* s){
if(s==NULL) return NULL;
char* pBegin=s;
char* pEnd=s;
while(*pEnd!='\0'){//先确定pEnd的位置
    pEnd++;
}
pEnd--;
reverse(pBegin,pEnd);//翻转整个句子
pBegin=s;
pEnd=s;
while(*pBegin!='\0'){
    if(*pBegin==' '){//如果是空格就往前
        pBegin++;
        pEnd++;
    }
    else if(*pEnd==' '||*pEnd=='\0'){//
        reverse(pBegin,--pEnd);//翻转这个单词
        pBegin=++pEnd;//pBegin越过当前这个单词
    }
    else{
        pEnd++;
    }
}
return s;
}
void reverse(char *pBegin,char *pEnd){
    if(pBegin==NULL||pEnd==NULL) return;
    while(pBegin<pEnd){
        char temp=*pBegin;
        *pBegin=*pEnd;
        *pEnd=temp;

        pBegin++;
        pEnd--;
    }
}

力扣上比较麻烦的是要处理空格,两个单词之间可能有多个空格,句子前和后也可能有空格。

双指针从后往前遍历,一个指针找第一个不是空格的位置,第二个指针从刚刚找到的位置出发找第一个空格的位置,把这之间的单词顺序存放在结果数组里(缺点就是新开辟了一个空间存放结果)

作者:helloworld-o
链接:https://leetcode-cn.com/problems/fan-zhuan-dan-ci-shun-xu-lcof/solution/chun-c-shuang-zhi-zhen-bian-li-xiang-xi-zhu-shi-qi/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

C语言处理字符串要非常小心,在这里j是有可能等于-1的

char* reverseWords(char* s){
int len=strlen(s);
if(len==0) return s;
int g=0;
int i=len-1;
int j=len-1;
while(s[g]==' '){
    g++;
}
printf("%d\n",g);
if(g==len) return "";
int index=0;
char* res=(char *)malloc(sizeof(char)*(len+1));
while(i!=g-1){
    while(i>=0 && s[i]==' ') i--;
//双指针从后往前遍历,一个 指针找第一个不是空格的位置,第二个指针从刚刚找到的位置出发找第一个空格的位置,把这之间的单词顺序存放在结果数组里(缺点就是新开辟了一个空间存放结果)
      j=i;
      while(j>=0 && s[j]!=' ') j--;
      printf("j+1=%d,i=%d\n",j+1,i);
      for(int k=j+1;k<=i;k++){//从j+1到i
       res[index++]=s[k];
       }
i=j;
res[index++]=' ';
}
//printf("%d\n",i);
//printf("%d\n",index);
res[index-1]='\0';//实际上是把最后的空格替换成结束符
return res;
}

 题目二:左旋转字符串

字符串的左旋转操作,我们可以利用翻转字符串的函数。

char* LeftRotateString(char* pStr,int n){
    if(pStr==NULL||n>strlen(pStr)) return NULL;
    char* b1=pStr;
    char* e1=pStr+n-1;
    char* b2=pStr+n;
    char* e2=pStr+strlen(pStr)-1;
    reverse(b1,e1);
    reverse(b2,e2);
    reverse(b1,e2);
    return pStr;
}

C++解法:substr的用法,第一个参数start是开始的索引,第二个是长度,如果如果没有写的话默认到最后一个字符。 如果不让用substr函数的话可以使用字符拼起来的方式

class Solution {
public:
    string reverseLeftWords(string s, int n) {
if(s.size()==0) return "";
if(n>s.size()) return "";//不合法的输入
string s1=s.substr(0,n);
string s2=s.substr(n);
return s2+s1;
    }
};

4.字符串转整数

这道题目在牛客网做过,但是牛客网的测试用例很简单。const int c1=42;   const int *p2=&ci;   //允许改变p2的值,这是一个底层const.   int i=0; int *const p1=&i;//不能改变p1的值,这是一个顶层const

之前用到的一个程序:这个c=(int)(unsigned char)*nptr++,这个c是不是变成了这个字符的ASCII值了? NULL的ASCII值是0?

static uint64_t  strTouint64(const char *nptr)
{
        int c;              /* 当前要转换的字符(一个一个字符转换成数字) */
        uint64_t total;         /* 当前转换结果 */
        int sign;           /* 标志转换结果是否带负号*/

        /*跳过空格,空格不进行转换*/
        while ( isspace((int)(unsigned char)*nptr) )
            ++nptr;
        c = (int)(unsigned char)*nptr++;//获取一个字符准备转换 
        sign = c;           /*保存符号标示*/
        if (c == '-' || c == '+')
            c = (int)(unsigned char)*nptr++;    /*跳过'+'、'-'号,不进行转换*/
        total = 0;//设置转换结果为0 
        while (isdigit(c)) {//如果字符是数字 
            total = 10 * total + (c - '0');     /* 根据ASCII码将字符转换为对应的数字,并且乘10累积到结果 */

            c = (int)(unsigned char)*nptr++;    /* 取下一个字符 */
        }
         //根据符号指示返回是否带负号的结果 
        if (sign == '-')
            return -total;
        else
            return total;  
}

可以观察一下这道题目的测试用例是怎么设计的:上面的程序是很值得参考的

没有通过的测试用例  2 000 000 000 000 000 000,该数用long long也已经不够了,所以必须要在while循环里进行判断。"-91283472332"     2147483648这个测试用例试了很久。

我想着应该有的测试用例:42 “    -42”  4193 with words, -2147483648, -2147483649, -2147483650,2147483647,2147483648,2147483649,-91283472332,2000000000000000000000.   0  "", + ,-,

class Solution {
public:
    int strToInt(string str) {
//这里的说明看不清楚-91283472332w这种应该是返回最小的INT吗,是的
//字符串为空的话,不需要进行转换
if(str.empty()) return 0;
int len=str.size();
int sign=0;//符号位
long long total=0;//当前转换结果
int i=0;
while(str[i]==' '){
i++;
}
if(i==len) return 0;//字符串仅包含空白字符
sign=(int)(unsigned char)str[i];//c是第一个不为空格的字符
//45是负号,43是正
cout<<sign<<endl;
if(str[i]=='+'||str[i]=='-') i++;
while(i<len && isdigit(str[i])){
    int pop=str[i]-'0';
    cout<<"pop="<<pop<<" ";
    if(sign=='-'){
        if( total>INT_MAX/10 || (total==INT_MAX/10 && pop>8 )) {
            return INT_MIN;
        }
    }
    else{//sign不为负
         if( total>INT_MAX/10 || (total==INT_MAX/10 && pop>7 )) {
             return INT_MAX;
         }
    }
    //if((sign=='+'&& total>INT_MAX/10) || (total==INT_MAX/10 && pop>7 && sign=='+')) return INT_MAX;  这样写是存在问题的
    //if((sign=='-'&& total>INT_MAX/10) || (total==INT_MAX/10 && pop>8 && sign=='-')) return INT_MIN;  这样写是存在问题的。"-91283472332"这个测试用例不过,应该返回-2147483648
    total=total*10+pop;
    cout<<"total="<<total<<endl;
    i++;
}
//假设该字符串中第一个非空格字符不是一个有效整数字符,或字符串仅包含空白字符
if(sign=='-') total=-total;
return total;
    }
};

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值