how can I make this C++ code more efficient - c++

This is a leetcode question about " Longest Substring Without Repeating Characters"
and I failed on 987th (It is the final test case)
the reason i fail is because "Time Limit Exceeded"
Can someone give me some advice to fix my code?
class Solution {
public:
int lengthOfLongestSubstring(string s) {
vector<char> answer;
vector<char> temp;
int len = s.length();
for ( int a = 0; a < len;a++ ){
for( int aa = a; aa < len;aa++ ){
temp.push_back(s[aa]) ;
if ( repeat(temp) ){
temp.pop_back();
changeAnswer(temp,answer);
temp.clear();
aa = len;
}
changeAnswer(temp,answer);
}
}
return answer.size();
}
bool repeat(vector<char> temp){
if ( temp.size() < 2 )
return false;
for ( int a = 0; a < temp.size();a++ )
for ( int aa = a+1; aa < temp.size(); aa++ )
if ( temp[aa] == temp[a] )
return true;
return false;
}
void changeAnswer( vector<char> &temp, vector<char> &answer ){
if ( temp.size()>answer.size() )
answer = temp;
}
};

It looks that you are using a complete brute force approach to solve the question. The time complexity of your solution is O(n^2), if 'n' is the length of the string. In the case of string length being in range 10^5, the time limit will definitely exceed. First, you should try to optimize your code to make it work in O(n) time.
Here is what you can do:
I think hashing would be helpful in this case. You can store the last occurrence of a character in a hashMap or unordered_map and check the current character if it is already there in the map. If so, you need to count only the characters after the last occurrence of this character. To keep track of from where you have to count the characters simply maintain a variable that stores the first index of a substring with unique characters(like 'p' that I have used in the code below).
Here is my implementation of the above:
int lengthOfLongestSubstring(string s) {
unordered_map<char, int> mp;
int n = (int)s.length();
if(n == 0){
return 0;
}
int res = 1;
int p = 0;
mp[s[0]] = 0;
for(int i = 1; i < n ; i++){
if(mp.find(s[i]) == mp.end()){
res = max(res, i-p+1);
mp[s[i]] = i;
}
else{
char ch = s[i];
int temp = mp[ch];
if(p <= temp)
p = temp + 1;
res = max(res, i - p + 1);
mp[ch] = i;
}
}
return res;
}
Try this, it should work.

Related

Resolving a Bug in My Code which is not passing the test cases

There is this problem on Leetcode , Link of the problem is : https://leetcode.com/problems/largest-time-for-given-digits/
I have written the code for this problem , and according to me my code is correct but still my code is not passing all the test cases and I am stuck debugging where is the issue in my Code .
Can Anybody please help me with this ?
class Solution {
public:
bool isValid(string s){
if(s[0] > '2') return false;
if(s[0] == '2'){
if(s[1] >= '4'){
return false ;
}
}
if(s[2] >=6) return false ;
return true ;
}
vector<vector<int>> permute(vector<int> &nums)
{
vector<vector<int>> result;
//Base Case For The Problem:
if (nums.size() <= 1)
return {nums};
for (int i = 0; i < nums.size(); i++)
{
vector<int> v(nums.begin(), nums.end());
v.erase(v.begin() + i);
auto res = permute(v);
for (int j = 0; j < res.size(); j++)
{
vector<int> _v = res[j];
_v.insert(_v.begin(), nums[i]);
result.push_back(_v);
}
}
return result;
}
string largestTimeFromDigits(vector<int>& A) {
vector<vector<int>> res ;
vector<string> valid ; //For Only Storing the Valid Time Permutations
res = permute(A);
//Now , Iterating Over All the Permutations:
for(int i=0 ; i<res.size() ; i++){
string curr = "";
for(int j=0 ; j<res[i].size() ; ++j){
curr += res[i][j];
}
if(isValid(curr)) valid.push_back(curr);
}
sort(valid.begin() , valid.end());
string ans = ""; //The Final Answer that we have to return at the end.
if(valid.size() > 0){
//Now , perform the Required Operations:
string temp = valid[valid.size() - 1];
ans = temp.substr(0,2) + ":" + temp.substr(2);
}
return ans;
}
};
Two problems in your code, both related to mixing int with char. The first is here:
if(s[2] >=6 ) {
return false ;
}
Because of this condition your isValid returns false always. No character in the range '0'...'9' is smaller than the integer 6. Compare the char to a char:
if(s[2] >='6' ) {
return false ;
}
Next, here
curr += res[i][j];
res[i][j] is an integer, but you want to add a character to the string:
curr += static_cast<char>(res[i][j]) + '0';
After fixing those two I get expected output at least for input {2,2,2,2}, see here: https://godbolt.org/z/35r3f9.
I have to mention that you would have found those problems yourself if you had used a debugger. Getting better in coding is not that much about making less mistakes, but about getting better at finding and fixing them. The debugger is an essential tool to do that.
C++
You can use std::prev_permutation and sort first:
// The following block might slightly improve the execution time;
// Can be removed;
static const auto __optimize__ = []() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cout.tie(nullptr);
return 0;
}();
// Most of headers are already included;
// Can be removed;
#include <cstdint>
#include <string>
#include <vector>
#include <algorithm>
static const struct Solution {
static const std::string largestTimeFromDigits(std::vector<int>& A) {
std::sort(std::begin(A), std::end(A), std::greater<int>());
do if (
(A[0] < 2 || A[0] == 2 && A[1] < 4) &&
A[2] < 6
) {
return std::to_string(A[0]) + std::to_string(A[1]) + ":" + std::to_string(A[2]) + std::to_string(A[3]);
}
while (std::prev_permutation(std::begin(A), std::end(A)));
return "";
}
};
Here is LeetCode's official solution in C++:
class Solution {
public:
string largestTimeFromDigits(vector<int>& A) {
int max_time = -1;
// prepare for the generation of permutations next.
std::sort(A.begin(), A.end());
do {
int hour = A[0] * 10 + A[1];
int minute = A[2] * 10 + A[3];
if (hour < 24 && minute < 60) {
int new_time = hour * 60 + minute;
max_time = new_time > max_time ? new_time : max_time;
}
} while(next_permutation(A.begin(), A.end()));
if (max_time == -1) {
return "";
} else {
std::ostringstream strstream;
strstream << std::setw(2) << std::setfill('0') << max_time / 60
<< ":" << std::setw(2) << std::setfill('0') << max_time % 60;
return strstream.str();
}
}
};
Alternative solution with regular expression:
This'd be difficult in C++ though:
class Solution:
def largestTimeFromDigits(self, A: List[int]) -> str:
for i in range(2359, -1, -1):
if i < 1000:
i = format(i, '04')
if int(re.findall(r'\d{2}$', str(i))[0]) > 59:
continue
l = list(map(int, str(i)))
for j in A:
if j in l:
l.remove(j)
if len(l) == 0:
hm = re.findall(r'.{2}', str(i))
return f'{hm[0]}:{hm[1]}'
return ""
Alternative solution using three loops in Java:
public final class Solution {
public static final String largestTimeFromDigits(
final int[] A
) {
String res = "";
for (int i = 0; i < 4; ++i) {
for (int j = 0; j < 4; ++j) {
for (int k = 0; k < 4; ++k) {
if (i == j || i == k || j == k) {
continue;
}
String hour = "" + A[i] + A[j];
String minute = "" + A[k] + A[6 - i - j - k];
String time = hour + ":" + minute;
if (
hour.compareTo("24") < 0 &&
minute.compareTo("60") < 0 &&
res.compareTo(time) < 0
) {
res = time;
}
}
}
}
return res;
}
}
References
For additional details, please see the Discussion Board where you can find plenty of well-explained accepted solutions with a variety of languages including low-complexity algorithms and asymptotic runtime/memory analysis1, 2.

What is the Big O of this implementaition

I'm trying to answer a question on leetcode https://leetcode.com/problems/palindromic-substrings/. I know the passing solution is an O(N^2) solution with dynamic programming. All the solutions I've seen have used a bottom-up approach. I tried to use a top-down approach and I thought I also accomplished O(N^2) but the online judge is giving me time limit exceeded error at the final test case which made me unsure if my implementation is indeed O(N^2). Can someone please tell me the correct big O of the below code?
bool dfs(string s, int start, int end, string curWord, int &count, unordered_map<string, int> &map)
{
if (start >= end)
{
return true;
}
string st = to_string(start) + " , " + to_string(end);
if (map.find(st) != map.end())
return map[st];
if (s[start] == s[end] && dfs(s, start + 1, end - 1, curWord, count, map))
{
count++;
map[st] = true;
}
else
{
map[st] = false;
}
return map[st];
}
int countSubstrings(string s)
{
string word = "";
int count = 0;
unordered_map<string, int> map;
for (int i = 0; i < s.length(); i++)
{
for (int j = s.length() - 1; j > i; j--)
{
dfs(s, i, j, word, count, map);
}
}
return count + s.length();
}

C++ Checking for duplicates in a string

I have a function counter(string s1, string s2) which takes two strings (both size 4) of numbers as its parameters. s1 is randomly generated while s2 is user-input.
int counter(string s1, string s2){
int count = 0;
int i, j;
if(i != j){
for(i = 0; i < 4; i++){
for(j = 0; j < 4; j++){
if(s2[i] != s2[j] && s2[j] == s1[i]){
count += 1;
}
}
}
}
return count;
}
What this function does is compare each element of s2 to s1's. If an element of s2 is equal to an element of s1, provided that they do not have the same index or position, count increments by 1. The function, however, encounters problems when s2 contains duplicates. For example, if s1 = 1234 and s2 = 4445, the output should be 1, but my program outputs 3. How should I be able to detect the duplicates in the string?
Forgot to mention this, but s1 (the randomly generated string) has distinct characters.
This would solve the problem you have when the same letter is present in S2 several times.
int counter(string s1, string s2){
int count = 0;
int i, j;
for(i = 0; i < 4; i++){
for(j = 0; j < 4; j++){
if(i != j){
if(s2[j] == s1[i]){
count += 1;
break; // Break out of inner for-loop
// to avoid counting letters twice
}
}
}
}
return count;
}
However, I'm not sure what you want the program to do in case the same letter is present in S1 several times.
EDIT:
After reading some of the clarifications in the comments, the if-statement should be
if(s2[i] != s2[j] && s2[j] == s1[i]){
like in the original post.
I dare suggest. But I broke my brain your question =)
int counter(string s1, string s2){
int count = 0;
for(int i = 0; i < s1.length(); ++i)
{
string::size_type loc = s2.find(s1[i], 0);
while (loc != string::npos)
{
if(loc != i)
++count;
loc = s2.find(s1[i], loc + 1);
}
}
return count;
}
add your if condition inside the for loop,
int counter(string s1, string s2){
int count = 0;
int i, j;
for(i = 0; i < 4; i++){
for(j = 0; j < 4; j++){
if(i!=j && s2[j] == s1[i]){
count += 1;
}
}
}
}
return count;
}
You can detect duplicate elements by implementing Levenshtein distance algorithms.
int levenshtein(const char *s, int ls, const char *t, int lt){
int a, b, c;
/* if either string is empty, difference is inserting all chars
* from the other
*/
if (!ls) return lt;
if (!lt) return ls;
/* if last letters are the same, the difference is whatever is
* required to edit the rest of the strings
*/
if (s[ls] == t[ls])
return levenshtein(s, ls - 1, t, lt - 1);
/* else try:
* changing last letter of s to that of t; or
* remove last letter of s; or
* remove last letter of t,
* any of which is 1 edit plus editing the rest of the strings
*/
a = levenshtein(s, ls - 1, t, lt - 1);
b = levenshtein(s, ls, t, lt - 1);
c = levenshtein(s, ls - 1, t, lt );
if (a > b) a = b;
if (a > c) a = c;
return a + 1;}

Trouble with finding a c-string substring in C++

long time lurker, first time poster. I have been working on this problem for the last six hours hours.
Problem:
Implement the following functions. Each function deals with null terminated C-Style strings. You can assume that any char array passed into the functions will contain null terminated data. Place all of the functions in a single file and then create a main() function that tests the functions thoroughly.
Note: You may not use any c-string functions other than strlen().
I am having trouble with the fourth function.
The desired behavior is: This function returns the index in string s where the substring can first be found. For example if s is "Skyscraper" and substring is "ysc" the function would return 2. It should return -1 if the substring does not appear in the string.
prototype:
int findSubstring(char *str, char substring[])
Here's my two starts for function definitions, I'm not really sure if either is going in the right direction, I'm having a lot of trouble keeping the loop iterations in my head, any help would be TREMENDOUSLY appreciated.
int findSubstring(char *str, char substring[]){
int subS = -1, index1 = 0, index2 = 0;
int length1 = (strlen(str) - 1);
int length2 = (strlen(substring) - 1);
if(length1 > length2){
for(int i = 0; i <= length2; i++){
for(int j = 0; j <= length1; j++){
if(*(substring + i) == *(str + j) && *(substring +i) != '\0' ){
i++;
if(index1 == 0){
index1 = i;
}
}
if( *(substring + i) == '\0'){
subS = i + 2;
}
}
}
}
if (length1 < length2){
cout << "Invalid, substring exceeds size of string!" << endl;
}
return subS;
}
int findSubstring(char *str, char substring[]){
int index = -1;
int lengthStr = (strlen(str) - 1);
int lengthSub = (strlen(substring) - 1);
if (lengthStr < lengthSub){
cout << "Invalid input, substring exceeds size of string!" << endl;
}
if( lengthSub == 0){
cout << "";
}
if (lengthStr > lengthSub){
for(int i = 0; i <= lengthSub; i++){
for(int j = 0; j <= lengthStr; j++){
}
return index;
}
//You can replace my str.size() and subString.size() by the size of each c-string.
int stringPointerOperation( string str, string subString )
{
int pos=0;
bool notFound;
for(int i = 0; i < str.size() ; i++)
{
notFound= false;
if(str[i] == subString[0])
{
pos=i;
for(int k = 0 ; k < subString.size() && k < str.size() ; k++,i++)
{
if(subString[k] != str[i] )
{
notFound=true;
break;
}
}
}
}
if(notFound)
return -1;
else
return pos;
}
You are using the wrong strategy for finding a sub-string in a string. The outer for loop needs to iterate over the main string and the inner for loop needs to iterate over the sub-string.
Say you are looking for "de" in "abcdef". The strategy that I find easier to understand and implement is:
Can I find "de" starting from 0 of "abcdef". No, I can't.
Can I find "de" starting from 1 of "abcdef". No, I can't.
Can I find "de" starting from 2 of "abcdef". No, I can't.
Can I find "de" starting from 3 of "abcdef". Yes, I can. Return 3.
Here's a version that works for me.
int findSubstring(char *str, char substring[]){
int i;
int j;
int length1 = strlen(str);
int length2 = strlen(substring);
if(length1 < length2){
std::cout << "Invalid, substring exceeds size of string!" << std::endl;
return -1;
}
for(i = 0; i < length1; i++){
for(j = 0; j < length2; j++){
// The index to use access the element of str
// needs to be offset by i.
if( str[i+j] != substring[j] )
{
break;
}
}
if ( j == length2 )
{
return i;
}
}
return -1;
}

Finding the largest palindrome in string implementation

I'm trying to solve a problem that asks to find the largest palindrome in a string up to 20,000 characters. I've tried to check every sub string whether it's a palindrome, that worked, but obviously was too slow. After a little googling I found this nice algorithm
http://stevekrenzel.com/articles/longest-palnidrome. I've tried to implement it, however I can't get it to work. Also the given string contains illegal characters, so I have to convert it to only legal characters and output the longest palindrome with all characters.
Here's my attempt:
int len = original.length();
int longest = 0;
string answer;
for (int i = 0; i < len-1; i++){
int lower(0), upper(0);
if (len % 2 == 0){
lower = i;
upper = i+1;
} else {
lower = i;
upper = i;
}
while (lower >= 0 && upper <= len){
string s2 = original.substr(lower,upper-lower+1);
string s = convert(s2);
if (s[0] == s[s.length()-1]){
lower -= 1;
upper += 1;
} else {
if (s.length() > longest){
longest = s.length();
answer = s2;
}
break;
}
}
}
I can't get it to work, I've tried using this exact algorithm on paper and it worked, please help. Here's full code if you need it : http://pastebin.com/sSskr3GY
EDIT:
int longest = 0;
string answer;
string converted = convert(original);
int len = converted.length();
if (len % 2 == 0){
for (int i = 0; i < len - 1; i++){
int lower(i),upper(i+1);
while (lower >= 0 && upper <= len && converted[lower] == converted[upper]){
lower -= 1;
upper += 1;
}
string s = converted.substr(lower+1,upper-lower-1);
if (s.length() > longest){
longest = s.length();
answer = s;
}
}
} else {
for (int i = 0; i < len; i++){
int lower(i), upper(i);
while (lower >= 0 && upper <= len && converted[lower] == converted[upper]){
lower -= 1;
upper += 1;
}
string s = converted.substr(lower+1,upper-lower-1);
if (s.length() > longest){
longest = s.length();
answer = s;
}
}
}
Okay so I fixed the problems, it works perfectly fine but only if the length of converted string is odd. Please help.
I can see two major errors:
Whether you initialise your upper/lower pointers to i,i or i,i+1 depends on the parity of the palindrome's length you want to find, not the original string. So (without any further optimisations) you'll need two separate loops with i going from 0 to len (len-1), one for odd palindrome lengths and another one for even.
The algorithms should be executed on the converted string only. You have to convert the original string first for it to work.
Consider this string: abc^ba (where ^ is an illegal character), the longest palindrome excluding illegal characters is clearly abcba, but when you get to i==2, and move your lower/upper bounds out by one, they will define the bc^ substring, after conversion it becomes bc, and b != c so you concede this palindrome can't be extended.
#include <iostream>
using namespace std;
int main()
{
string s;
cin >> s;
signed int i=1;
signed int k=0;
int ml=0;
int mi=0;
bool f=0;
while(i<s.length())
{
if(s[i]!=s[i+1])
{
for(k=1;;k++)
{
if(!(s[i-k]==s[i+k] && (i-k)>=0 && (i+k)<s.length()))
{
break;
}
else if(ml < k)
{
ml=k;
mi=i;
f=1;
}
}
}
i++;
}
i=0;
while(i<s.length())
{
if(s[i]==s[i+1])
{
for(k=1;;k++)
{
if(!(s[i-k]==s[k+1+i] && (i-k)>=0 && (k+i)<s.length()))
{
break;
}
else if(ml < k)
{
ml=k;
mi=i;
}
}
}
i++;
}
if(ml < 1)
{
cout << "No Planidrom found";
return 0;
}
if(f==0)
{
cout << s.substr(mi-ml,2*ml+2);
}
else
{
cout << s.substr(mi-ml,2*ml+1);
}
return 0;
}
#biziclop : As you said.. i used 2 while loops. one for even and one for old palindrom string. finally i was able to fix it. thanks for your suggestion.
public void LongestPalindrome()
{
string str = "abbagdghhkjkjbbbbabaabbbbbba";
StringBuilder str1=new StringBuilder();
StringBuilder str2= new StringBuilder();
for (int i = 0; i < str.Length; i++)
{
str1.Append((str[i]));
for (int j = i + 1; j < str.Length; j++)
{
str1.Append((str[j]));
if (Checkpalindrome(str1))
{
str2.Append(str1);
str2.Append(" ");
}
}
str1.Clear();
}
var Palstr = str2.ToString().Split(' ');
var Longestpal = Palstr.Where(a => a.Length >= (Palstr.Max(y => y.Length)));
foreach (var s in Longestpal)
{
Console.WriteLine(s);
}
}
public bool Checkpalindrome(StringBuilder str)
{
string str1 = str.ToString();
StringBuilder str2=new StringBuilder();
var revstr = str1.Reverse();
foreach (var c in revstr )
{
str2.Append(c);
}
if (str1.Equals(str2.ToString()))
{
return true;
}
return false;
}