Tag Archives: 字符串

LeetCode Solve the Equation

LeetCode Solve the Equation Solve a given equation and return the value of x in the form of string “x=#value”. The equation contains only ‘+’, ‘-‘ operation, the variable xand its coefficient. If there is no solution for the equation, return “No solution”. If there are infinite solutions for the equation, return “Infinite solutions”. If there is exactly one solution for the equation, we ensure that the value of x is an integer. Example 1:

Input: "x+5-3+x=6+x-2"
Output: "x=2"
Example 2:
Input: "x=x"
Output: "Infinite solutions"
Example 3:
Input: "2x=x"
Output: "x=0"
Example 4:
Input: "2x+3x-6x=x+2"
Output: "x=-1"
Example 5:
Input: "x=x+2"
Output: "No solution"

解一个一元一次方程。简单题,首先把方程分解成等号两边两个子表达式,然后解析这两个子表达式,使成为la+lb*x=ra+rb*x,再转换为(lb-rb)x=ra-la,令lb-rb=b, ra-la=a,则最终的表达式等价于bx=a。 如果b等于0,且a也等于0,则有无穷多解。如果b等于0,但a不等于0,则无解。否则x=a/b。 所以问题的难点是怎样把一个表达式转换为a+bx的形式。也就是代码中的convert函数。遍历字符串,取出每个带符号的操作数,如果该操作数包含’x’,则取出系数,加到b上;否则是常数项,加到a上。 有一个需要注意的点是包含’x’的操作数可能是’-x’或者’+x’,提取系数时需要特殊判断。 代码如下: [cpp] class Solution { private: void convert(string& s, int& a, int& b) { int aa = 0, bb = 0; s += "+"; int start = 0, end = 0; for (end = 0; end < s.size(); ++end) { if (end != 0 && (s[end] == ‘+’ || s[end] == ‘-‘)) { // -x=-1 string tmp = s.substr(start, end – start); if (tmp.find(‘x’) != string::npos) { if (tmp == "x" || tmp == "+x")bb += 1; else if (tmp == "-x")bb -= 1; else bb += stoi(tmp.substr(0, tmp.size() – 1)); } else { aa += stoi(tmp); } start = end; } } a = aa; b = bb; } public: string solveEquation(string equation) { size_t pos = equation.find(‘=’); string left = equation.substr(0, pos), right = equation.substr(pos + 1); int la = 0, lb = 0, ra = 0, rb = 0; convert(left, la, lb); convert(right, ra, rb); int b = lb – rb, a = ra – la; if (b == 0) { if (a == 0)return "Infinite solutions"; else return "No solution"; } else { return "x=" + to_string(a / b); } } }; [/cpp] 本代码提交AC,用时3MS。]]>

LeetCode Design Log Storage System

LeetCode Design Log Storage System You are given several logs that each log contains a unique id and timestamp. Timestamp is a string that has the following format: Year:Month:Day:Hour:Minute:Second, for example, 2017:01:01:23:59:59. All domains are zero-padded decimal numbers. Design a log storage system to implement the following functions: void Put(int id, string timestamp): Given a log’s unique id and timestamp, store the log in your storage system. int[] Retrieve(String start, String end, String granularity): Return the id of logs whose timestamps are within the range from start to end. Start and end all have the same format as timestamp. However, granularity means the time level for consideration. For example, start = “2017:01:01:23:59:59”, end = “2017:01:02:23:59:59”, granularity = “Day”, it means that we need to find the logs within the range from Jan. 1st 2017 to Jan. 2nd 2017. Example 1:

put(1, "2017:01:01:23:59:59");
put(2, "2017:01:01:22:59:59");
put(3, "2016:01:01:00:00:00");
retrieve("2016:01:01:01:01:01","2017:01:01:23:00:00","Year"); // return [1,2,3], because you need to return all logs within 2016 and 2017.
retrieve("2016:01:01:01:01:01","2017:01:01:23:00:00","Hour"); // return [1,2], because you need to return all logs start from 2016:01:01:01 to 2017:01:01:23, where log 3 is left outside the range.
Note:
  1. There will be at most 300 operations of Put or Retrieve.
  2. Year ranges from [2000,2017]. Hour ranges from [00,23].
  3. Output for Retrieve has no order required.

设计一个日志系统,该系统有两个操作,put(id,timestamp)把timestamp时刻的日志id放到日志系统中,retrieve(start,end,gra)从系统中取出timestamp范围在[start,end]之间的日志id,时间的粒度是gra。 我设计的系统是这样的,为了方便retrieve,系统中的日志都按timestamp排序了。有趣的是,在zero-padded(每部分不足补前导0)的情况下,timestamp的字符串排序就是timestamp表示的时间的排序。 定义一个Node结构体,保持一个日志,信息包括日志id和timestamp。用一个链表存储所有Node,并且当新Node插入时,采用插入排序的方法使得链表始终有序。 retrieve的时候,根据粒度,重新设置start和end,比如样例中粒度为Year时,把start改为Year固定,其他时间最小
"2016:00:00:00:00:00"
把end改为Year固定,其他时间最大
"2017:12:31:23:59:59"
这样我只需要遍历链表,把所有timestamp字符串在这个范围内的日志id取出来就好了。其他粒度也是类似的。 完整代码如下: [cpp] class LogSystem { private: struct Node { int id; string timestamp; Node(int i, string t) :id(i), timestamp(t) {}; }; list<Node> log; string start, end; public: LogSystem() { start = "2000:00:00:00:00:00"; end = "2017:12:31:23:59:59"; } void put(int id, string timestamp) { Node node(id, timestamp); if (log.empty())log.push_back(node); else { auto it = log.begin(); while (it != log.end() && (*it).timestamp <= timestamp)++it; log.insert(it, node); } } vector<int> retrieve(string s, string e, string gra) { if (gra == "Year") { s = s.substr(0, 4) + start.substr(4); e = e.substr(0, 4) + end.substr(4); } else if (gra == "Month") { s = s.substr(0, 7) + start.substr(7); e = e.substr(0, 7) + end.substr(7); } else if (gra == "Day") { s = s.substr(0, 10) + start.substr(10); e = e.substr(0, 10) + end.substr(10); } else if (gra == "Hour") { s = s.substr(0, 13) + start.substr(13); e = e.substr(0, 13) + end.substr(13); } else if (gra == "Minute") { s = s.substr(0, 16) + start.substr(16); e = e.substr(0, 16) + end.substr(16); } vector<int> ans; auto it = log.begin(); while (it != log.end() && (*it).timestamp < s)++it; while (it != log.end() && (*it).timestamp <= e) { ans.push_back((*it).id); ++it; } return ans; } }; [/cpp] 本代码提交AC,用时19MS。]]>

LeetCode Validate IP Address

LeetCode Validate IP Address Write a function to check whether an input string is a valid IPv4 address or IPv6 address or neither. IPv4 addresses are canonically represented in dot-decimal notation, which consists of four decimal numbers, each ranging from 0 to 255, separated by dots (“.”), e.g.,172.16.254.1; Besides, leading zeros in the IPv4 is invalid. For example, the address 172.16.254.01 is invalid. IPv6 addresses are represented as eight groups of four hexadecimal digits, each group representing 16 bits. The groups are separated by colons (“:”). For example, the address 2001:0db8:85a3:0000:0000:8a2e:0370:7334 is a valid one. Also, we could omit some leading zeros among four hexadecimal digits and some low-case characters in the address to upper-case ones, so 2001:db8:85a3:0:0:8A2E:0370:7334 is also a valid IPv6 address(Omit leading zeros and using upper cases). However, we don’t replace a consecutive group of zero value with a single empty group using two consecutive colons (::) to pursue simplicity. For example, 2001:0db8:85a3::8A2E:0370:7334 is an invalid IPv6 address. Besides, extra leading zeros in the IPv6 is also invalid. For example, the address 02001:0db8:85a3:0000:0000:8a2e:0370:7334 is invalid. Note: You may assume there is no extra space or special characters in the input string. Example 1:

Input: "172.16.254.1"
Output: "IPv4"
Explanation: This is a valid IPv4 address, return "IPv4".
Example 2:
Input: "2001:0db8:85a3:0:0:8A2E:0370:7334"
Output: "IPv6"
Explanation: This is a valid IPv6 address, return "IPv6".
Example 3:
Input: "256.256.256.256"
Output: "Neither"
Explanation: This is neither a IPv4 address nor a IPv6 address.

给定一个字符串,判断这个字符串是否符合IPv4或者IPv6的格式。 简单的字符串题目。对于IPv4,需要满足:
  1. 只包含数字和点号
  2. 包含4个part,每个part用点号分隔
  3. 每个part不能有前导0,且数值范围是[0,255]
对于IPv6,需要满足:
  1. 只包含数字、冒号和a~f或者A~F
  2. 包含8个part,每个part用冒号分隔
  3. 每个part可以有前导0,但长度不超过4
把这些规则理清楚,用C++ string写代码,如下: [cpp] class Solution { private: bool checkIPv4Part(const string& part) { size_t len = part.size(); if (len < 1 || len > 3)return false; if (len > 1 && part[0] == ‘0’)return false; for (const auto& c : part) { if (!(c >= ‘0’&&c <= ‘9’))return false; } int v = stoi(part); return v >= 0 && v <= 255; } bool isIPv4(string& IP) { IP += "."; int parts = 0; size_t start = 0; while (true) { size_t pos = IP.find(‘.’, start); if (pos == string::npos)break; if (!checkIPv4Part(IP.substr(start, pos – start)))return false; ++parts; start = pos + 1; } return parts == 4; } bool checkIPv6Part(const string& part) { size_t len = part.size(); if (len < 1 || len > 4)return false; for (const auto& c : part) { if (!((c >= ‘0’&&c <= ‘9’) || (c >= ‘a’&&c <= ‘f’) || (c >= ‘A’&&c <= ‘F’)))return false; } return true; } bool isIPv6(string& IP) { IP += ":"; int parts = 0; size_t start = 0; while (true) { size_t pos = IP.find(‘:’, start); if (pos == string::npos)break; if (!checkIPv6Part(IP.substr(start, pos – start)))return false; ++parts; start = pos + 1; } return parts == 8; } public: string validIPAddress(string IP) { if (IP.find(‘.’) != string::npos) return isIPv4(IP) ? "IPv4" : "Neither"; else return isIPv6(IP) ? "IPv6" : "Neither"; } }; [/cpp] 本代码提交AC,用时3MS。]]>

LeetCode Remove K Digits

LeetCode Remove K Digits Given a non-negative integer num represented as a string, remove k digits from the number so that the new number is the smallest possible. Note:

  • The length of num is less than 10002 and will be ≥ k.
  • The given num does not contain any leading zero.
Example 1:
Input: num = "1432219", k = 3
Output: "1219"
Explanation: Remove the three digits 4, 3, and 2 to form the new number 1219 which is the smallest.
Example 2:
Input: num = "10200", k = 1
Output: "200"
Explanation: Remove the leading 1 and the number is 200. Note that the output must not contain leading zeroes.
Example 3:
Input: num = "10", k = 2
Output: "0"
Explanation: Remove all the digits from the number and it is left with nothing which is 0.

给定一个数字字符串,要求从中删除k个数字,使得最终结果最小,返回最小的数字字符串。 使用栈和贪心的思路。为了使结果尽量小,我们从左往右扫描字符串,把字符存到一个栈中,如果当前字符比栈顶小,则可以把栈顶数字删掉,这样相当于用当前字符代替栈顶字符所在位置,使得结果更小了。否则把当前字符压栈,注意此时不能把当前字符删掉,因为可能后面还有更大的字符出现。 如果这样过了一遍之后,还是没删够k个字符,此时,字符串从左往右肯定是非降序的。所以我们依次弹栈,把栈顶的元素删掉,直到删够k个字符。 最后还要判断一下剩余字符串是否为空,并删除前导零。完整代码如下: [cpp] class Solution { public: string removeKdigits(string num, int k) { if (k >= num.size())return "0"; string ans = ""; for (const auto& c : num) { if (ans.empty() || k == 0)ans.push_back(c); else { while (!ans.empty() && ans.back() > c) { ans.pop_back(); if (–k == 0)break; } ans.push_back(c); } } while (k– > 0 && !ans.empty()) ans.pop_back(); while (!ans.empty() && ans[0] == ‘0’)ans.erase(ans.begin()); return ans.empty() ? "0" : ans; } }; [/cpp] 本代码提交AC,用时6MS。]]>

LeetCode Next Greater Element III

LeetCode Next Greater Element III Given a positive 32-bit integer n, you need to find the smallest 32-bit integer which has exactly the same digits existing in the integer n and is greater in value than n. If no such positive 32-bit integer exists, you need to return -1. Example 1:

Input: 12
Output: 21
Example 2:
Input: 21
Output: -1

给定一个数n,要求用和n一样的数字,重组成一个比n大的最小的数。如果不存在这样的数,输出-1。 比如样例1,n=12,则同样用数字1和2,重组出比12大的最小的数就是21。如果n本身等于21,则无法用数字1和2重组出比21更大的数了。 首先很容易想到无解的情况,比如n=76543210,因为n的数字从左到右是非升序的,所以n本身已经是最大的了,无法重组出比n更大的数。所以隐隐约约感觉可以从左往右找第一个上升的位置。 再来,n=76546743,如果从左往右找第一个上升的位置,则是i=3的数字4,4->6是上升的,所以可以考虑从4后面找比4大的最小的数,也就是6,交换4和6,变成76564743,因为原来为4的位置已经变成更大的6了,所以6后面最小即可,对6后面的数排序,变成了76563447。 但是76563447并不是比n大的最小的数,另外还有76547346,也就是不变4,而是变i=4的数字6。所以换一种思路,我们从右往左找第一个下降的位置,比如这里是i=5的数字7,然后从该位置往右找一个比i-1的数6大的最小的数7,交换他们的位置,同时把i-1右边的数从小到大排序。 再比如n=12443322,从右往左找第一个下降的位置是i=2,数字为4,从该位置找一个比i-1的数2大的最小的数是i=4的数3,交换他们n=1344222,再对i-1右边的数从小到大排序,得到n=1322244。 完整代码如下,另外需要注意结果不能超出int范围,所以先转换为long long,再和INT_MAX比较。 [cpp] class Solution { public: int nextGreaterElement(int n) { string s = to_string(n); int m = s.size(); for (int i = m – 1; i > 0; –i) { if (s[i] > s[i – 1]) { int pos = i; for (int j = i; j < m; ++j) { if (s[j] > s[i – 1] && s[j] < s[pos]) { pos = j; } } swap(s[pos], s[i – 1]); sort(s.begin() + i, s.end()); long long ans = stoll(s); if (ans <= INT_MAX)return ans; else return -1; } } return -1; } }; [/cpp] 本代码提交AC,用时3MS。]]>

LeetCode Basic Calculator II

LeetCode Basic Calculator II Implement a basic calculator to evaluate a simple expression string. The expression string contains only non-negative integers, +, -, *, / operators and empty spaces . The integer division should truncate toward zero. You may assume that the given expression is always valid. Some examples:

"3+2*2" = 7
" 3/2 " = 1
" 3+5 / 2 " = 5
Note: Do not use the eval built-in library function.
本题是LeetCode Basic Calculator的简化版,去掉了括号,增加了乘除法。但是算法都是一模一样的,先把中缀转换为后缀,然后用栈求值。 代码如下: [cpp] // 优先级 unordered_map<string, int> priority = { {"+", 1},{"-", 1},{"*", 2},{"/", 2} }; class Solution { private: void push(stack<string> &numbers, stack<string> &opers, const string& numOrop) { if (isdigit(numOrop[0])) { numbers.push(numOrop); } else { if (numOrop == ")") { while (opers.top() != "(") { numbers.push(opers.top()); opers.pop(); } opers.pop(); } else if (numOrop == "(") { opers.push(numOrop); } else if (opers.empty() || opers.top() == "(") { opers.push(numOrop); } else if (priority[numOrop] > priority[opers.top()]) { opers.push(numOrop); } else { while (!opers.empty() && opers.top() != "(" && priority[numOrop] <= priority[opers.top()]) { numbers.push(opers.top()); opers.pop(); } opers.push(numOrop); } } } // 中缀转后缀表达式 list<string> in2post(const string& s) { stack<string> numbers, opers; int start = 0, end = 1, n = s.size(); while (start < n) { while (start < n&&s[start] == ‘ ‘)++start; if (start >= n)break; if (isdigit(s[start])) { end = start + 1; while (end < n&&isdigit(s[end]))++end; string num = s.substr(start, end – start); push(numbers, opers, num); start = end; } else { string op = string(1, s[start]); push(numbers, opers, op); ++start; } } while (!opers.empty()) { numbers.push(opers.top()); opers.pop(); } list<string> ans; while(!numbers.empty()) { ans.push_front(numbers.top()); numbers.pop(); } return ans; } // 求值后缀表达式的值 int eval(list<string>& post) { int ans = 0; stack<int> numbers; for (const auto& s : post) { if (priority.find(s) != priority.end()) { int num2 = numbers.top(); numbers.pop(); int num1 = numbers.top(); numbers.pop(); if (s == "+")numbers.push(num1 + num2); else if (s == "-")numbers.push(num1 – num2); else if (s == "*")numbers.push(num1*num2); else numbers.push(num1 / num2); } else numbers.push(stoi(s)); } return numbers.top(); } public: int calculate(string s) { list<string> ans = in2post(s); return eval(ans); } }; [/cpp] 本代码提交AC,用时85MS。]]>

LeetCode Basic Calculator

224. Basic Calculator 224. Basic Calculator

Implement a basic calculator to evaluate a simple expression string.

The expression string may contain open ( and closing parentheses ), the plus + or minus sign -non-negative integers and empty spaces .

Example 1:

Input: "1 + 1"
Output: 2

Example 2:

Input: " 2-1 + 2 "
Output: 3

Example 3:

Input: "(1+(4+5+2)-3)+(6+8)"
Output: 23

Note:

  • You may assume that the given expression is always valid.
  • Do not use the eval built-in library function.

给定一个字符串表达式,要求求解该表达式的值。 使用栈解决。我们通常看到的表达式称为中缀表达式,需要把它转换为后缀表达式,比如1+((2+3)×4)-5转换为1 2 3 + 4 × + 5 -。我们可以很容易用栈求解后缀表达式,只需要遍历后缀表达式,把遇到的操作数压栈,只要遇到运算符,就从栈中弹出两个操作数,进行运算,再把运算结果压栈。最后栈中只能得到一个值,就是整个表达式的值。 所以问题的关键就是怎样把中缀表达式转换为后缀表达式。 使用两个栈,一个栈保存操作数numbers,另一个栈保存运算符opers。遍历原字符串,按如下规则进行压栈和弹栈:

  • 如果是操作数,直接压入numbers
  • 如果是操作符op:
    1. 如果op为左括号“(”,则直接把op压入opers
    2. 如果op为右括号“)”,则把opers的运算符不断弹栈并压入numbers,直到栈顶为左括号“(”,此时将这一对括号丢弃
    3. 如果opers为空,或者opers栈顶为左括号“(”,则直接把op压入opers
    4. 此时,栈顶肯定不可能是括号了,如果op的优先级高于opers栈顶的优先级,则直接把op压入opers
    5. 否则,把opers栈顶的运算符不断弹栈并压入numbers,直到栈顶为左括号,或者op的优先级高于opers栈顶的优先级。把op压入opers

如果是操作符op,下面的1~5的判断规则是有先后顺序的,需要先判断是否为括号,再判断是否是一般的运算符。 运算符的优先级是,+-优先级相同,*/优先级相同,且+-的优先级低于*/。 得到中缀表达式的后缀表达式之后,再用开头提到的栈求解。后缀表达式的求值就非常简单了。 完整代码如下:

// 优先级
unordered_map<string, int> priority = { { "+", 1 }, { "-", 1 }, { "*", 2 }, { "/", 2 } };
class Solution {
private:
    void push(stack<string>& numbers, stack<string>& opers, const string& numOrop)
    {
        if (isdigit(numOrop[0])) {
            numbers.push(numOrop);
        }
        else {
            if (numOrop == ")") {
                while (opers.top() != "(") {
                    numbers.push(opers.top());
                    opers.pop();
                }
                opers.pop();
            }
            else if (numOrop == "(") {
                opers.push(numOrop);
            }
            else if (opers.empty() || opers.top() == "(") {
                opers.push(numOrop);
            }
            else if (priority[numOrop] > priority[opers.top()]) {
                opers.push(numOrop);
            }
            else {
                while (!opers.empty() && opers.top() != "(" && priority[numOrop] <= priority[opers.top()]) {
                    numbers.push(opers.top());
                    opers.pop();
                }
                opers.push(numOrop);
            }
        }
    }
    // 中缀转后缀表达式
    list<string> in2post(const string& s)
    {
        stack<string> numbers, opers;
        int start = 0, end = 1, n = s.size();
        while (start < n) {
            while (start < n && s[start] == ‘ ‘)
                ++start;
            if (start >= n)
                break;
            if (isdigit(s[start])) {
                end = start + 1;
                while (end < n && isdigit(s[end]))
                    ++end;
                string num = s.substr(start, end – start);
                push(numbers, opers, num);
                start = end;
            }
            else {
                string op = string(1, s[start]);
                push(numbers, opers, op);
                ++start;
            }
        }
        while (!opers.empty()) {
            numbers.push(opers.top());
            opers.pop();
        }
        list<string> ans;
        while (!numbers.empty()) {
            ans.push_front(numbers.top());
            numbers.pop();
        }
        return ans;
    }
    // 求值后缀表达式的值
    int eval(list<string>& post)
    {
        int ans = 0;
        stack<int> numbers;
        for (const auto& s : post) {
            if (priority.find(s) != priority.end()) {
                int num2 = numbers.top();
                numbers.pop();
                int num1 = numbers.top();
                numbers.pop();
                if (s == "+")
                    numbers.push(num1 + num2);
                else if (s == "-")
                    numbers.push(num1 – num2);
                else if (s == "*")
                    numbers.push(num1 * num2);
                else
                    numbers.push(num1 / num2);
            }
            else
                numbers.push(stoi(s));
        }
        return numbers.top();
    }
public:
    int calculate(string s)
    {
        list<string> ans = in2post(s);
        return eval(ans);
    }
};

本代码提交AC,用时92MS。
参考:http://blog.csdn.net/antineutrino/article/details/6763722

二刷。push函数实际上只有3个分支就可以了,上面的过于复杂,看下面:

unordered_map<string, int> priority = { {"+",1},{"-",1},{"*",2},{"/",2} };

class Solution {
private:
	bool IsDigit(char c) {
		return c >= '0'&&c <= '9';
	}

	vector<string> ParseString(const string &s) {
		vector<string> vec;
		int n = s.size();
		int i = 0, j = 0;
		while (i < n) {
			while (i < n&&s[i] == ' ')++i;
			if (i >= n)break;

			if (!IsDigit(s[i])) {
				vec.push_back(string(1, s[i]));
				++i;
			}
			else {
				j = i + 1;
				while (j < n&&IsDigit(s[j]))++j;
				vec.push_back(s.substr(i, j - i));
				i = j;
			}
		}
		return vec;
	}

	list<string> In2Post(const vector<string> &vec) {
		stack<string> numbers, opers;
		for (int i = 0; i < vec.size(); ++i) {
			if (IsDigit(vec[i][0])) {
				numbers.push(vec[i]);
			}
			else {
				if (vec[i] == ")") { // 右括号
					while (opers.top() != "(") {
						numbers.push(opers.top());
						opers.pop();
					}
					opers.pop();
				}
				else if (vec[i] == "(") { // 左括号
					opers.push(vec[i]);
				}
				else { // 操作符
					while (!opers.empty() && opers.top() != "("&&priority[vec[i]] <= priority[opers.top()]) {
						numbers.push(opers.top());
						opers.pop();
					}
					opers.push(vec[i]);
				}
			}
		}
		
		while (!opers.empty()) {
			numbers.push(opers.top());
			opers.pop();
		}
		list<string> lst;
		while (!numbers.empty()) {
			lst.push_front(numbers.top());
			numbers.pop();
		}
		return lst;
	}

	int Evaluate(const list<string> &lst) {
		stack<string> stk;
		for (list<string>::const_iterator it = lst.begin(); it != lst.end(); ++it) {
			if (IsDigit((*it)[0])) {
				stk.push(*it);
			}
			else {
				int b = atoi(stk.top().c_str());
				stk.pop();
				int a= atoi(stk.top().c_str());
				stk.pop();
				if (*it == "+") {
					stk.push(to_string(a + b));
				}
				else if (*it == "-") {
					stk.push(to_string(a - b));
				}
			}
		}
		return atoi(stk.top().c_str());
	}

public:
	int calculate(string s) {

		vector<string> vec = ParseString(s);
		list<string> lst = In2Post(vec);
		return Evaluate(lst);
	}
};

本代码提交AC,用时312MS。

LeetCode Unique Substrings in Wraparound String

LeetCode Unique Substrings in Wraparound String Consider the string s to be the infinite wraparound string of “abcdefghijklmnopqrstuvwxyz”, so s will look like this: “…zabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcd….”. Now we have another string p. Your job is to find out how many unique non-empty substrings of p are present in s. In particular, your input is the string p and you need to output the number of different non-empty substrings of p in the string s. Note: p consists of only lowercase English letters and the size of p might be over 10000. Example 1:

Input: "a"
Output: 1
Explanation: Only the substring "a" of string "a" is in the string s.
Example 2:
Input: "cac"
Output: 2
Explanation: There are two substrings "a", "c" of string "cac" in the string s.
Example 3:
Input: "zab"
Output: 6
Explanation: There are six substrings "z", "a", "b", "za", "ab", "zab" of string "zab" in the string s.

给定一个a-z的无限循环字符串s,比如…zabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcd….。再给定一个字符串p,问p有多少个子串是在s中的。 在s中的子串需要满足一定的条件,比如单个字符可以,还有就是连续的字符串,即abcd…之类的。 注意第二个样例中,p有两个相同的子串c,但是结果中只统计一次,所以比如p中有多个以同一个字符结尾的子串,我们只需要统计最长的那个就好了。比如P=”abcdefgxxxxcdefgyyy”。以g结尾的连续子串有两个,分别是abcdefg和cdefg,但是我们只需要保留前者就好了,因为前者最长,它的所有子串已经包含后者的所有子串了。而子串的长度就是以g为结尾且在s中的子串的长度,比如这里就是7。 所以我们只需要统计以每个字符结尾的最长连续子串的长度,然后把所有长度加起来,就是最终结果。 注意abcdefg以g结尾的长度是7,也就是abcdefg有7个子串是在s中的。abcdefg也包含abcdef,以f结尾的长度是6,也就是abcdef有6个子串在s中。 代码如下: [cpp] class Solution { public: int findSubstringInWraproundString(string p) { vector<int> count(26, 0); int len = 0; for (int i = 0; i < p.size(); ++i) { if (i > 0 && (p[i] – p[i – 1] == 1 || p[i] – p[i – 1] == -25))++len; else len = 1; count[p[i] – ‘a’] = max(count[p[i] – ‘a’], len); } int ans = 0; for (int i = 0; i < 26; ++i)ans += count[i]; return ans; } }; [/cpp] 本代码提交AC,用时9MS。]]>

LeetCode Word Break

139. Word Break

Given a non-empty string s and a dictionary wordDict containing a list of non-empty words, determine if s can be segmented into a space-separated sequence of one or more dictionary words.

Note:

  • The same word in the dictionary may be reused multiple times in the segmentation.
  • You may assume the dictionary does not contain duplicate words.

Example 1:

Input: s = "leetcode", wordDict = ["leet", "code"]
Output: true
Explanation: Return true because "leetcode" can be segmented as "leet code".

Example 2:

Input: s = "applepenapple", wordDict = ["apple", "pen"]
Output: true
Explanation: Return true because "applepenapple" can be segmented as "apple pen apple".
             Note that you are allowed to reuse a dictionary word.

Example 3:

Input: s = "catsandog", wordDict = ["cats", "dog", "sand", "and", "cat"]
Output: false

给定一个词典和一个字符串,问字符串能否拆分成若干个词典中有的词。比如样例中词典中有leet和code,则给定的字符串leetcode就能拆分成leet和code,他们都在词典中。
使用DP思路。假设dp[j]表示字符串下标j之前的字符串能否拆分成词典中的词的组合。如果要求dp[i],则我们可以看看i之前的所有j,如果有dp[j]是true的,且[j,i)的子串在dict中,说明[0,i)也能被拆分成词典中的词,所以dp[i]=true。最后我们只需要返回dp[n]即可。
对于s=”leetcode”,dp=1,0,0,0,1,0,0,0,1,所以dp[n]=1。
代码如下:

class Solution {
public:
    bool wordBreak(string s, vector<string>& wordDict)
    {
        unordered_set<string> dict(wordDict.begin(), wordDict.end());
        int n = s.size();
        vector<int> dp(n + 1, 0);
        dp[0] = 1;
        for (int i = 1; i <= n; ++i) {
            for (int j = i – 1; j >= 0; –j) {
                if (dp[j] && dict.find(s.substr(j, i – j)) != dict.end()) {
                    dp[i] = 1;
                    break;
                }
            }
        }
        return dp[n];
    }
};

本代码提交AC,用时3MS。

LeetCode Add Bold Tag in String

LeetCode Add Bold Tag in String Given a string s and a list of strings dict, you need to add a closed pair of bold tag <b> and </b> to wrap the substrings in s that exist in dict. If two such substrings overlap, you need to wrap them together by only one pair of closed bold tag. Also, if two substrings wrapped by bold tags are consecutive, you need to combine them. Example 1:

Input:
s = "abcxyz123"
dict = ["abc","123"]
Output:
"<b>abc</b>xyz<b>123</b>"
Example 2:
Input:
s = "aaabbcc"
dict = ["aaa","aab","bc"]
Output:
"<b>aaabbc</b>c"
Note:
  1. The given dict won’t contain duplicates, and its length won’t exceed 100.
  2. All the strings in input have length in range [1, 1000].

给定一个字符串s和一个字典dict,如果dict中的单词是s的子串,则要对这个子串加<b></b>的标签。如果有多个重叠的子串都需要加标签,则这些标签需要合并。比如样例2中,子串aaa、aab和bc重叠了,则他们的标签合并,最终结果就是<b>aaabbc</b>c。 这一题其实比第三题要简单。 因为有多个子串可能重叠,为了方便,设置一个长度和s一样的数组flag,flag[i]=1表示第i位需要被标签包围,flag[i]=0表示不需要被标签包围。 把dict中的每个字符串,去s中找,判断是否是s的子串,如果是,则把子串对应的flag位置1。当dict中的所有字符串都查完了。最后遍历flag,把连续1对应的子串加上标签就好了。 这里字符串匹配查找我直接用stl的find,自己写kmp也是可以的。完整代码如下: [cpp] class Solution { public: string addBoldTag(string s, vector<string>& dict) { int n = s.size(); string flag(n, ‘0’); for (const auto& sub : dict) { size_t pos = s.find(sub, 0); while (pos != string::npos) { fill(flag.begin() + pos, flag.begin() + pos + sub.size(), ‘1’); pos = s.find(sub, pos + 1); } } string ans = ""; int i = 0, j = 0; while (flag[i] == ‘0’)++i; ans += s.substr(0, i); while (i < n) { int j = i + 1; while (j < n&&flag[j] == ‘1’)++j; ans += "<b>" + s.substr(i, j – i) + "</b>"; if (j >= n)break; i = j; while (j < n&&flag[j] == ‘0’)++j; ans += s.substr(i, j – i); i = j; } return ans; } }; [/cpp] 本代码提交AC,用时22MS。]]>