leetcode-cn | 字符串

验证回文串

给定一个字符串,验证它是否是回文串,只考虑字母和数字字符,可以忽略字母的大小写。

说明:本题中,我们将空字符串定义为有效的回文串。

示例 1:

输入: "A man, a plan, a canal: Panama"
输出: true

示例 2:

输入: "race a car"
输出: false
class Solution {
    public boolean isPalindrome(String s) {
        //replace是替换单个的字符串,replaceAll是使用的正则表达式,用方括号括起来
        s=s.replaceAll("[^a-zA-Z0-9]","");
        System.out.println(s);
        int length=s.length();
        int mid=length/2;
        boolean even;
        if(length%2==0) even=true;
        else even=false;
        
        Stack<Character> stack=new Stack<Character>();
        char[] array=s.toCharArray();
        for(int i=0;i<mid;i++){
            stack.push(array[i]);
        }
        
        if(!even) mid++;
        for(int i=mid;i<length;i++){
            char c=stack.pop();
            System.out.print(c);
            System.out.println(array[i]);
            if(!equals(array[i],c))
                return false;
        }
        
        return true;
        
    }
    
    public boolean equals(char a,char b){
        //System.out.println(a+' '+b);
        if(Character.isUpperCase(a))
            a=Character.toLowerCase(a);
        if(Character.isUpperCase(b))
            b=Character.toLowerCase(b);
        if(a==b) return true;
        else return false;
    } 
}

用的是栈,但其实感觉还是有一点小题大做了,直接访问对应位置的字符其实就可以了。


分割回文串

给定一个字符串 s,将 s 分割成一些子串,使每个子串都是回文串。

返回 s 所有可能的分割方案。

示例:

输入: "aab"
输出:
[
  ["aa","b"],
  ["a","a","b"]
]
class Solution {
    public List<List<String>> partition(String s) {
        //最初是尝试使用动态规划求所有的字符串组合,但是似乎并不合适,动态规划用来求最大回文串长度
//         List<List<String>> result = new ArrayList<List<String>>();
//         char[] chars=s.toCharArray();
//         int length=s.length();
//         boolean[][] dp=new boolean[length][length];    //使用动态规划,dp[i][j]表示从i到j的子串是否为回文串
        
//         for(int i=0;i<length;i++){
//             dp[i][i]=true;
//         }
//         for(int i=0;i<length-1;i++){
//             if(chars[i]==chars[i+1])
//                 dp[i][i+1]=true;
//             else
//                 dp[i][i+1]=false;
//         }
        
//         for(int i=0;i<length;i++){  //遍历每一行
//             for(int l=2;l<length;l++){
//                 for(int j=0;j<length;j++){
//                     if(l+j>=length)
//                         break;
//                     if(dp[j+1][l+j-1]==false)
//                         dp[i][l+j]=false;
//                     else if(chars[j]==chars[l+j])
//                         dp[i][l+j]=true;
//                     else
//                         dp[i][l+j]=false;
//                 }
//             }
//         }
        
//         for(int i=0;i<length;i++){
//             ArrayList<String> vector=new ArrayList<String>();
//             for(int j=i;j<length;j++){
//                 if(dp[i][j]==true)
//                     vector.add(s.substring(i,j));
//             }
//             result.add(vector);
//         }
        
//         for(int i=0;i<length;i++){
//             for(int j=0;j<length;j++){
//                 System.out.print(dp[i][j]);
//                 System.out.print(" ");
//                 System.out.println("");
//             }
//         }
        
//         return result;
    }
}

选择使用回溯法遍历得到所有的可能,也就是深度优先搜索。

class Solution:
    # 是要用到DFS(深度优先搜索算法)
    # result = []

    def isPartition(self, s: str):
        start = 0
        end = len(s) - 1
        while (start <= end):
            if (s[start] == s[end]):
                start += 1
                end -= 1
            else:
                return False
        return True

    def nextWords(self, s: str, index: int, List,result):
        if (index == len(s)):
            temp2=List.copy()
            result.append(temp2)
            return
        for i in range(index, len(s)):
            temp = s[index:i + 1]
            if (self.isPartition(temp)):
                List.append(temp)
                self.nextWords(s, i + 1, List,result)
                del List[len(List) - 1]

    def partition(self, s: str):
        result=[]
        r = []
        self.nextWords(s, 0, r,result)
        return result

决定选择放弃Java选择Python来写算法,但是发现确实有很多坑

  • 列表在传递的时候要用 .copy() 函数(a=b.copy()),这样对a的修改就不会影响b,因为其实所有的nextWords()函数使用的都是相同的一个list,也就是在partition中定义的r,如果不使用copy函数就会出现最终的返回值是一堆空的列表。
  • LeetCode提交代码之后进行验证的方法应该是创建一个这个类的对象,并重复调用主方法,在最开始的时候result定义的是类内的变量,就会出现上一种情况的执行情况仍然会保存在result中,改为函数类内的变量问题得以解决。Java中就没有这个问题,还是要再多熟悉一些,加油啦

分割字符串

给定一个非空字符串 s 和一个包含非空单词列表的字典 wordDict,判定 s 是否可以被空格拆分为一个或多个在字典中出现的单词。

说明:

  • 拆分时可以重复使用字典中的单词。
  • 你可以假设字典中没有重复的单词。
示例 1:
输入: s = "leetcode", wordDict = ["leet", "code"]
输出: true
解释: 返回 true 因为 "leetcode" 可以被拆分成 "leet code"。
示例 2:
输入: s = "applepenapple", wordDict = ["apple", "pen"]
输出: true
解释: 返回 true 因为 "applepenapple" 可以被拆分成 "apple pen apple"。
     注意你可以重复使用字典中的单词。
示例 3:
输入: s = "catsandog", wordDict = ["cats", "dog", "sand", "and", "cat"]
输出: false

看到这个题目的最初的想法是和分割回文串的思想是一样的,用回溯法来解答,但是就会出现这样一种情况,s="aaaaaaaaaaaaaaaaaaaaaaaaaaaab",wordDict=['a','aaa','aaaaaa'] 类似这样的情况会导致时间复杂度很高,一直要经历对于‘a’的考虑,没有通过submit。

另一种思路是使用动态规划的方法,dp[i]表示长度为 i 的字符串是否可以被分割成单词,这样只需考虑前面的长度的情况,而不需要考虑前面是怎么分割的,不需要考虑回溯

class Solution:
    def wordBreak(self, s: str, wordDict) -> bool:
        dp = [False for i in range(len(s) + 1)]  # dp[i]表示长度为i的字符串是否可以拆分
        dp[0] = True
        for i in range(1, len(s) + 1):
            self.getDp(dp, i, wordDict, s)
        #print(dp)
        return dp[len(s)]

    def getDp(self, dp, index, wordDict, s):  # dp[index]
        for i in range(index - 1, -1, -1):
            if dp[i] == True:
                ss=s[i:index]
                if s[i:index] in wordDict:
                    dp[index] = True
                    # 这里在刚开始写的时候忘记return了,结果就是一直是false
                    return
        dp[index] = False

分割回文串Ⅱ

给定一个非空字符串 s 和一个包含非空单词列表的字典 wordDict,在字符串中增加空格来构建一个句子,使得句子中所有的单词都在词典中。返回所有这些可能的句子。

说明:

  • 分隔时可以重复使用字典中的单词。
  • 你可以假设字典中没有重复的单词。
示例 1:
输入:
s = "catsanddog"
wordDict = ["cat", "cats", "and", "sand", "dog"]
输出:
[
  "cats and dog",
  "cat sand dog"
]
示例 2:
输入:
s = "pineapplepenapple"
wordDict = ["apple", "pen", "applepen", "pine", "pineapple"]
输出:
[
  "pine apple pen apple",
  "pineapple pen apple",
  "pine applepen apple"
]
解释: 注意你可以重复使用字典中的单词。
示例 3:
输入:
s = "catsandog"
wordDict = ["cats", "dog", "sand", "and", "cat"]
输出:
[]

思路与上一个题目是一样的,只不过是 dp 中存放的是上一个是可拆分串的长度的列表,即 dp[ i ] = [ j, k ] 表示长度为 i 的可拆分串是在长度为 j 或长度为 k 的可拆分串上构成的。

class Solution:
    def wordBreak(self, s: str, wordDict: List[str]) -> List[str]:
        dp=[[] for i in range(len(s)+1)]
        results=[[] for i in range(len(s))]
        dp[0].append(-1)
        for i in range(1,len(s)+1):
            self.getDp(dp,i,wordDict,s)
        return self.getResult(dp,results,s)
    
    def getDp(self,dp,index,wordDict,s):
        for i in range(index-1,-1,-1):
            if len(dp[i])!=0:
                ss=s[i:index]
                if s[i:index] in wordDict:
                    dp[index].append(i)
    
    def getResult(self,dp,results,s):
        for i in dp[len(s)]:
            result=[s[i:len(s)]]
            self.getR(dp,results,s,i,result)
        temp=[]
        for i in results:
            if len(i)!=0:
                s=''
                for item in i:
                    s=s+item+' '
                s=s.strip()
                temp.append(s)
        results=temp.copy()
        print(temp)
        return temp
        
    def getR(self,dp,results,s,index,result):
        if index==0:
            results.append(result)
            return
        for i in dp[index]:
            r=result.copy()
            r.insert(0,s[i:index])
            self.getR(dp,results,s,i,r)

另外需要看清题目,输出的结果是字符串的列表,不是列表的列表,还要注意的就是添加每个字符串中的单词的顺序。


实现 Trie (前缀树)

实现一个 Trie (前缀树),包含 insertsearch, 和 startsWith 这三个操作。

Trie trie = new Trie();

trie.insert("apple");
trie.search("apple");   // 返回 true
trie.search("app");     // 返回 false
trie.startsWith("app"); // 返回 true
trie.insert("app");   
trie.search("app");     // 返回 true

说明:

  • 你可以假设所有的输入都是由小写字母 a-z 构成的。
  • 保证所有输入均为非空字符串。
struct TrieNode {
	//static constexpr size_t kAsciiCount = 256;
	TrieNode(char value)
		:value(value), isEnd(false), isRoot(false){
	}
	bool isRoot;
	bool isEnd;
	char value;
	vector<TrieNode*> subNodes;
};

class Trie {
public:
	TrieNode* root;

	/** Initialize your data structure here. */
	Trie() {
		root = new TrieNode('R');
		(*root).isRoot = true;
	}

	/** Inserts a word into the trie. */
	void insert(string word) {
		if (word.empty())
			return;

		TrieNode* current = root;
		for (char c : word) {
			TrieNode* temp = getNode(c, current->subNodes);
			if (temp == NULL)
			{
				TrieNode* newNode = new TrieNode(c);
				current->subNodes.push_back(newNode);
				current = newNode;
			}
			else
				current = temp;
		}
		current->isEnd = true;
	}

	TrieNode* getNode(char c, vector<TrieNode*> subNodes) {
		if (subNodes.size() == 0)
			return NULL;
		for (TrieNode* node : subNodes) {
			if (node->value == c)
				return node;
		}
		return NULL;
	}

	
	/** Returns if the word is in the trie. */
	bool search(string word) {
		TrieNode* current = root;
		for (char c : word) {
			TrieNode* temp = getNode(c, current->subNodes);
			if (temp == NULL)
				return false;
			else
				current = temp;
		}
		return current->isEnd;
	}

	/** Returns if there is any word in the trie that starts with the given prefix. */
	bool startsWith(string prefix) {
		TrieNode* current = root;
		for (char c : prefix) {
			TrieNode* temp = getNode(c, current->subNodes);
			if (temp == NULL)
				return false;
			else
				current = temp;
		}
		return true;
	}
};

/**
 * Your Trie object will be instantiated and called as such:
 * Trie obj = new Trie();
 * obj.insert(word);
 * bool param_2 = obj.search(word);
 * bool param_3 = obj.startsWith(prefix);
 */

用C++的话就要好好区分传引用,传值和传地址的区别了。

字典树又称单词查找树,Trie树,是一种树形结构,是一种哈希树的变种。典型应用是用于统计,排序和保存大量的字符串(但不仅限于字符串),所以经常被搜索引擎系统用于文本词频统计。它的优点是:利用字符串的公共前缀来减少查询时间,最大限度地减少无谓的字符串比较,查询效率比哈希树高。(https://baike.baidu.com/item/字典树/9825209?fr=aladdin)


单词搜索Ⅱ


有效的字母异位词

给定两个字符串 st ,编写一个函数来判断 t 是否是 s 的一个字母异位词。

示例 1:
输入: s = "anagram", t = "nagaram"
输出: true
示例 2:
输入: s = "rat", t = "car"
输出: false
说明:
你可以假设字符串只包含小写字母。
进阶:
如果输入字符串包含 unicode 字符怎么办?你能否调整你的解法来应对这种情况?

所谓的字母异位词的意思就是组成两个字符串的字母是完全相同的,只不过个数是不同的,不需要想的很复杂了

class Solution {
    //没有理解字母异位词的含义是什么,意思是只要是那些字母就可以了
    //map是C++中的容器,它提供一对一的哈希表,第一个为关键字,只可以出现一次,第二个为键值,map内部自建一棵红黑树
public:
    bool isAnagram(string s, string t) {
        if(s.length()!=t.length())
            return false;
        map<char,int> sCount,tCount;
        map<char,int>::iterator it;
        for(int i=0;i<s.length();i++){
            char c=s.at(i);
            if(sCount.find(c)==sCount.end()){
                sCount.insert(pair<char,int>(c,1));
            }
            else{
                sCount[c]+=1;
            }
        }
        for(int i=0;i<t.length();i++){
            char c=t.at(i);
            if(tCount.find(c)==tCount.end()){
                tCount.insert(pair<char,int>(c,1));
            }
            else{
                tCount[c]+=1;
            }
        }
        
        //比较两个字符串中的字符的个数
        //使用迭代器对map进行遍历
        it=sCount.begin();
        while(it!=sCount.end()){
            if(tCount[it->first]!=it->second)
                return false;
            it++;
        }
        return true;
        
        
    }
};

在这里使用了用map存储的方式,存入和读取起来都是很慢的,但是这样做解决了如果字符中有Unicode编码的字符如何解决的问题。

其他的解决方式

  • 按照题目已知,给每个字符串定义一个长度为26的数组存储当前字符的个数。(无法解决存在Unicode编码的情况)
  • 分别将两个字符串中的字符进行排序,检查对应位置是否相同

字符串中的第一个唯一字符

给定一个字符串,找到它的第一个不重复的字符,并返回它的索引。如果不存在,则返回 -1。

案例:
s = "leetcode"
返回 0.

s = "loveleetcode",
返回 2.

题目给的提示说可以假设只包含小写字母。

其实是一个很简单的题目,自己想的太过复杂了,其实只需要使用哈希表记录每个字符出现的次数,然后再从头开始遍历一次这个字符串,检查字符的出现次数是否为1.

class Solution {
public:
    //bool find(char,vector<char>);
    int firstUniqChar(string s) {
        //最初选择的方法是将字符串转化为字符串数组后进行排序,再找出唯一的字符的下标,但那样返回的是排序之后的下标
        
        ///第三种,建立一个以26个字母为下标的哈希表
        int count[26];
        for(int i=0;i<26;i++)
            count[i]=0;
        for(int i=0;i<s.length();i++){
            count[s.at(i)-'a']++;
        }
        
        for(int i=0;i<s.length();i++){
            if(count[s.at(i)-'a']==1)
                return i;
        }
        return -1;
    }
};

再第一次运行这个代码的时候发生了生成错误的问题,原因是少了一个括号!


反转字符串

编写一个函数,其作用是将输入的字符串反转过来。输入字符串以字符数组 char[] 的形式给出。

不要给另外的数组分配额外的空间,你必须原地修改输入数组、使用 O(1) 的额外空间解决这一问题。

你可以假设数组中的所有字符都是 ASCII 码表中的可打印字符。

示例 1:
输入:["h","e","l","l","o"]
输出:["o","l","l","e","h"]
示例 2:
输入:["H","a","n","n","a","h"]
输出:["h","a","n","n","a","H"]
class Solution {
public:
    void reverseString(vector<char>& s) {
        int len=s.size();
        char temp;
        for(int i=0;i<len/2;i++){
            temp=s[i];
            s[i]=s[len-1-i];
            s[len-1-i]=temp;
        }
    }
};

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值