剑指Offer刷题-字符串处理/搜索

字符串的处理

机器人的运动范围

题目描述

地上有一个m行和n列的方格。一个机器人从坐标0,0的格子开始移动,每一次只能向左,右,上,下四个方向移动一格,但是不能进入行坐标和列坐标的数位之和大于k的格子。 例如,当k为18时,机器人能够进入方格(35,37),因为3+5+3+7 = 18。但是,它不能进入方格(35,38),因为3+5+3+8 = 19。请问该机器人能够达到多少个格子?

思路

做的时候不能忘记了,如果某个各自是限制访问的数字,则不能在这个各自上进行上下左右的搜索

class Solution {
public:
    int availableGrid = 0;
    int r = 0;
    int c = 0;
    int k = 0;
    int deltaI[4] = {-1,0,+1,0};
    int deltaJ[4] = {0,+1,0,-1};
    bool largerThanK(int k, int i, int j){
        int res=0;
        while(i){
            res+=(i%10);
            i/=10;
        }
        while(j){
            res+=(j%10);
            j/=10;
        }
        if(res>k)return false;
        return true;
    }
    void search(int i,int j,vector<vector<int>>&map){
        if(i<0||i>=r||j<0||j>=c){
            return ; // 越界就不在搜索
        }
        if(map[i][j]!=-1||!largerThanK(k,i,j)){
            return; // 当前位置访问过或者不复合条件则停止搜索 不在进行下面的搜索
        }
        map[i][j] = 1;
        availableGrid++;
        for(int di=0;di<4;di++){
            //printf("下一格:%d,%d",i+deltaI[di],j+deltaJ[di]);
            search(i+deltaI[di],j+deltaJ[di],map);
        }
        // 这个问题是不需要回溯的,能访问的格子会被慢慢的访问完。   
    }
    int movingCount(int threshold, int rows, int cols)
    {
        k = threshold;
        r = rows;
        c = cols;
        // map用来记录格子是否访问过
        vector< vector<int> >map(rows,vector<int>(cols,-1));
        search(0,0,map);
        return availableGrid;
    }
};

矩阵中的路径

题目描述

请设计一个函数,用来判断在一个矩阵中是否存在一条包含某字符串所有字符的路径。路径可以从矩阵中的任意一个格子开始,每一步可以在矩阵中向左,向右,向上,向下移动一个格子。如果一条路径经过了矩阵中的某一个格子,则该路径不能再进入该格子。 例如
在这里插入图片描述
矩阵中包含一条字符串"bcced"的路径,但是矩阵中不包含"abcb"路径,因为字符串的第一个字符b占据了矩阵中的第一行第二个格子之后,路径不能再次进入该格子。

思路

深度优先搜索+回溯

class Solution {
public:
    char *map = 0; // 以map[c*i+j] 的方式 实现map[i][j]
    int r = 0;     // 地图的行数
    int c = 0;     // 地图的列数
    int len = 0;// 目标字符串的长度
    int dir[5] = {-1,0,1,0,-1}; // 巧妙的方向控制 x+d[i] y+d[i+1] for i=0~3 就能实现 上右下左
    bool dfs(int i, int j, int pos, char* str){
        /**
            需要传入参数与 i j 当前的位置
            pos 已匹配的字符长度 【这个同时也是当前需要匹配的目标字符的下标!!!】
            str 目标字符串
        */
        // 1.边界判断
        if(i<0||i>=r||j<0||j>=c){
            // 说明越界了还是没有找到,
            return false;
        }
        // 2.判断是否访问过(题目要求访问过的不能再次访问) 访问过的字符会被替换成'#'
        // 判断当前的字符是否符合要求 未访问过+与目标字符串中下一个字符相等
        char curCh = map[i*c + j];
        if(curCh=='#'||curCh!=str[pos]){
            return false; // 当前位置访问过 或是 与当前需要匹配的目标字符不相等 都返回false
        }
        // 3.如果当前位置没有被访问过且与当前需要匹配的目标字符相等
        // 3.1 需要检查是否匹配到最后
        if(pos+1==len){
            return true;
        }
        // 3.2 如果没有匹配到最后 继续搜索
        map[i*c + j] = '#'; // 设置成特殊字符 表示访问过
        for(int di=0;di<4;di++){
            // 这里不需要去改变pos 直接传入一个pos就不用做pos的回溯了
            if(dfs(i+dir[di],j+dir[di+1],pos+1,str))return true;
        }
        // 4.如果当前位置四个方向都匹配失败,说明从当前位置开始匹配剩下的部分失败
        // 4.1 当前位置设置成未访问过的形式
        map[i*c + j] = curCh;
        // 4.2 返回匹配失败
        return false;
    }
    bool hasPath(char* matrix, int rows, int cols, char* str)
    {
        map = matrix;
        r = rows;
        c = cols;
        len = strlen(str); // 传入一个字符指针 返回字符串的长度 注意函数名不要和变量名重名
        for(int i=0;i<rows;i++){
            for(int j=0;j<cols;j++){
                if(dfs(i,j,0,str)){
                    return true;
                }
            }
        }
        return false;
    }
};

字符串的排列

题目描述

输入一个字符串,按字典序打印出该字符串中字符的所有排列。例如输入字符串abc,则按字典序打印出由字符a,b,c所能排列出来的所有字符串abc,acb,bac,bca,cab和cba。
输入一个字符串,长度不超过9(可能有字符重复),字符只包括大小写字母。

思路

深搜+回溯 的 排列组合 同时还要去重

class Solution {
public:
    void perm(string str, int pos, vector<string>&res){
        // 1.当前遍历到最后一个字符位置
        if(pos+1==str.size()){
            res.push_back(str);
            return ;
        }
        // 2.将str[pos]到最后一个字符 都放在pos上 然后全排列pos之后的字符串 perm(s,pos+1,res);
        // 这里是遍历从pos开始的后缀子串的起始字符
        for(int i=pos;i<str.size();i++){
            // 2.1如果pos到i这个区间里面 有字符和str[i]相等,比如str[i]=a,pos到i之间已经有个a了 这种情况就是重复了 跳过
            if(check(str, pos, i))continue;
            // 2.2 让str[pos]到最后一个字符 都在pos上待一次
            swap(str[pos], str[i]);
            // 2.3 接下来只交换str中pos之后的部分
            perm(str, pos+1, res);
            // 2.4 回溯 以便原str[pos]与下一个str[i]交换 pos与i之间的字符顺序都是和str进来的时候一样的
            swap(str[pos], str[i]);
        }
    }
    bool check(string str, int start, int end){
        // str[end]是待放到pos处的字符,如果pos到end之间有出现过该字符,则重复,不再搜索,返回true,当for continue
        for(int i=start; i<end;i++){
            if(str[i]==str[end])return true;
        }
        // pos到end之间没有出现过该字符,返回false搜索
        return false;
    }
    vector<string> Permutation(string str) {
        vector<string>res;
        if(str=="")return res;
        perm(str, 0, res);
        // 需要按照字典序排序 也可以使用set去重 按字典序排序
        sort(res.begin(), res.end());
        return res;
    }
};

把字符串转换成整数

题目描述

将一个字符串转换成一个整数,要求不能使用字符串转换整数的库函数。 数值为0或者字符串不是一个合法的数值则返回0

思路

1)先取出符号,方便处理,返回结果的时候再将符号加上
2)主要是针对范围非法的数字的判断
// 这里需要排除下字符串正好等于 -LLONG_MAX-1等情况
// -LLONG_MAX-1 ~ 0 ~ LLONG_MAX

class Solution {
public:
    int StrToInt(string str) {
        if(str.size()==0)return 0;
        long long res=0;
        int i=0;
        char ch = str[0];
        int flag=0; // 符号标记 正数为0,负数为1
        // 1.先判断数的符号
        // 1.1如果第一个字符不是 + - 号或是数组 直接就是非法字符返回0
        if(ch!='+'&&ch!='-'&&!(ch-'0'>=0&&ch-'0'<=9))return 0;
        if(ch=='+'||ch=='-'){
            i = 1;
            if(ch=='-')flag=1;
        }
        // 2.开始判断整串数是否合法
        for(;i<str.size();i++){
            ch = str[i];
            // 2.1 扫描到非数字字符就返回0
            if(!(ch-'0'>=0&&ch-'0'<=9))return 0;
            else{
           		// 2.2 先将原结果res*10再加上当前的数 判断是否移出
           		// 如果res*10比res还小,说明说已经超出最大能表示的范围
                // 这里LLONG_MAX=最末尾不是0或者9 所以*10不可能正好变成最大值
                if(res*10<res)return 0;
                res*=10;
                // 2.3 加上当前扫描到的数,要判断是否溢出
                if(res+(ch-'0')<res){
                	// -LLONG_MAX-1 ~ 0 ~ LLONG_MAX
                    // 这里需要排除下字符串正好等于 -LLONG_MAX-1的情况
                    if(res==LLONG_MAX&&(ch-'0')==1&&flag==1&&i==str.size()-1){
                        res+=1; // 【整数最大+1之后会变成负数最小】-LLONG_MAX-1
                        return res;
                    }else return 0;
                }
                res+=(ch-'0');
            }
        }
        if(flag==1)return -res;
        return res; 
    }
};

表示数值的字符串

题目描述

请实现一个函数用来判断字符串是否表示数值(包括整数和小数)。例如,字符串"+100",“5e2”,"-123",“3.1416"和”-1E-16"都表示数值。 但是"12e",“1a3.14”,“1.2.3”,"±5"和"12e+4.3"都不是。

class Solution {
public:
    bool isNumeric(char* string)
    {
        // 1.先判断是否含有e E 返回其所在的位置
        int i=0;
        int containE = -1;
        while(string[i]!='\0'){
            // printf("%c",string[i]);
            if(string[i]=='e'||string[i]=='E'){
                containE = i;
            }
            i++;
        }
        if(containE==i-1)return false;
        if(containE==i-2){
            if(string[i-1]-'0'<0||string[i-1]-'0'>9){
                return false;
            }
        }
        int strLen = i;
        int pointAppear = 0;
        int end;
        if(containE==-1)end = strLen;
        else{
            end = containE;
        }
        for(int j=0;j<end;j++){

            if(string[j]=='+'||string[j]=='-'){
                if(j!=0)return false;
            }
            if(string[j]!='+'&&string[j]!='-'&&(string[j]-'0'<0||string[j]-'0'>9)&&string[j]!='.'){
                return false;
            }
            if(string[j]=='.'){
                if(j==0||j==containE-1){
                    //cout<<"1"<<endl;
                    return false;
                }
                if((string[j-1]-'0'<0||string[j-1]-'0'>9)&&string[j-1]!='+'&&string[j-1]!='-'){
                    //cout<<"2"<<endl;
                    return false;
                }
                if(string[j+1]-'0'<0||string[j+1]-'0'>9){
                    //cout<<"3"<<endl;
                    return false;
                }
                if(pointAppear==1){
                    //cout<<"4"<<endl;
                    return false;
                }
                else{
                    pointAppear=1;
                }
            }
        }
        //cout<<strLen<<endl; 
        for(int j=containE+1;j<strLen&&containE!=-1;j++){
            //cout<<"判断右侧"<<endl; 
            if(string[j]=='+'&&string[j]=='-'){
                if(j!=containE+1)return false;
            }
            if(string[j]!='+'&&string[j]!='-'&&(string[j]-'0'<0||string[j]-'0'>9)){
                return false;
            }
        }

        return true;
    }
};

翻转单词顺序列

题目描述

牛客最近来了一个新员工Fish,每天早晨总是会拿着一本英文杂志,写些句子在本子上。同事Cat对Fish写的内容颇感兴趣,有一天他向Fish借来翻看,但却读不懂它的意思。例如,“student. a am I”。后来才意识到,这家伙原来把句子单词的顺序翻转了,正确的句子应该是“I am a student.”。Cat对一一的翻转这些单词顺序可不在行,你能帮助他么?

思路

class Solution {
public:
    string ReverseSentence(string str) {
        if(str==""||str.size()==1)return str;
        stack<string>charS;
        int flag = 0;
        int begin=-1;
        int end=-1;
        string res="";
        // 1.先预处理下,处理掉两端的空白
        for(int i=0;i<str.size();i++){ 
            if(str[i]!=' '){
                begin = i;
                break;
            }
        }
        for(int i=str.size()-1;i>=0;i--){
            if(str[i]!=' '){
                end = i;
                break;
            }
        }
        // 2.处理字符全为空格的情况
        if(begin==-1)return str;
        // 3.正式处理
        string tmp = "";
        for(int i=0;i<str.size();i++){
            if(str[i]==' '){
                if(res=="")res = tmp + res;
                else res = tmp + " " + res;
                tmp="";
            }else{
                tmp.push_back(str[i]);
            }
            if(i==end){
                if(res=="")res = tmp + res;
                else res = tmp + " " + res;
            }
        }
        /*
        // 4.这里可以处理 结果需要保留原字符串中空格的情况
        while(begin--){
            res = res + " ";
        }
        while((str.size()-1-end--)){
            res = " " + res;
        }
        */
        return res;
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值