Find in Custom Class Vector - c++

I am practicing this code(from LeetCode) to be better in C++. Unfortunately, I am unable to get 'find' to work correctly.
This code is used to search word from a vector of vector of type char (i.e. board) without visiting the same letter twice(visitedSoFar keeps a track of the x,y positions of the letters visitedSoFar).
A vector of class Node is used to store the positions visited so far.
Here is the code snippet I have written:
class Node{
private:
int x;
int y;
public:
Node(int a, int b):x(a),y(b){};
bool operator==(Node newNode){
if(this->x == newNode.x && this->y == newNode.y)
return true;
else
return false;
}
};
class Solution {
public:
bool exist(vector<vector<char>>& board, string word) {
vector <Node> visitedSoFar;
for(int r =0; r< board.size(); r++){
for(int c=0; c<board[r].size(); c++){
if(board[r][c] == word.at(0)){
if(search(board, word, visitedSoFar, board[r].size(), r, c))
return true;
}
}
}
return false;
}
private:
bool search(vector<vector<char>>& board, string word, vector<Node>& visitedSoFar, int size, int r, int c){
Node newNode(r,c);
visitedSoFar.push_back(newNode);
if(word.size() == 1)
return true;
Node toSearch1(r-1,c);
if(r-1 >= 0 && find(visitedSoFar.begin(), visitedSoFar.end(), toSearch1) == visitedSoFar.end()){
if(board[r-1][c] == word.at(1))
if(search(board, word.substr(1), visitedSoFar, size, r-1, c))
return true;
}
Node toSearch2(r+1,c);
if(r+1 < size && find(visitedSoFar.begin(), visitedSoFar.end(), toSearch2) == visitedSoFar.end()){
if(board[r+1][c] == word.at(1))
if(search(board, word.substr(1), visitedSoFar, size, r+1, c))
return true;
}
Node toSearch3(r,c-1);
if(c-1 >= 0 && find(visitedSoFar.begin(), visitedSoFar.end(), toSearch3) == visitedSoFar.end()){
if(board[r][c-1] == word.at(1))
if(search(board, word.substr(1), visitedSoFar, size, r, c-1))
return true;
}
Node toSearch4(r,c+1);
if(c+1 < size && find(visitedSoFar.begin(), visitedSoFar.end(), toSearch4) == visitedSoFar.end()){
if(board[r][c+1] == word.at(1))
if(search(board, word.substr(1), visitedSoFar, size, r, c+1))
return true;
}
visitedSoFar.pop_back();
return false;
}
};
If I comment the find I get the correct output, but this would not work for all test cases.
Thank You.
Edit
In method search, corrected if statement to check against size for (r+1) and (c+1).
Edit
The word can be constructed from letters of sequentially adjacent cell, where "adjacent" cells are those horizontally or vertically neighboring. The same letter cell may not be used more than once.
Edit
Design Error: The find operation should not be able to find(indicating that the node has not been visited so far) to then proceed with search in it. Hence altered find to be == visitedSoFar.end() rather than != visitedSoFar.end().

I think you should use a simpler design of your solution.
The idea behind checking every boardpoint is most likely unnecessary work, right? Using your approach you are constantly checking if the work has already been done. This checking includes a linear search through your board (every node will be saved at some time) for every search step. This means pretty much you can avoid checking it most likely, since the work to be done is almost the same.
Therefor a fastly coded solution would be like this.
bool row_contains_word(vector<char> const& row, string word)
{
if(word.size() > row.size())
throw std::invalid_argument("Word is longer then the row!!");
// linear search for the word in board
for(int i = 0; i < row.size() - word.size(); ++i) // start point
{
// check realtive to the start point if its the word there
for(int j = 0; j < word.size(); ++j)
{
if(row[i+j] != word[j])
break; // we can continue to check the next position
// last position we check here, and match means its the word
else if(j == (word.size() - 1) && row[i+j] == word[j])
return true;
}
}
return false;
Using this function (i dont think its a really good way to accomplish it, but just to have an example) you can simply loop:
for(int r = 0; r < board.size(); ++r)
{
if(row_contains_word(board[r], word))
return true;
}
// and same with colums as well
As mentioned in the comments, Solution isnt a candidate for a class.
It could be written like this:
namespace Solution
{
bool search(vector<vector<char>> const& board, string word); // implementaion follows
namespace // anonymous namespace, not accessible from the outside world, but only this compilation unit(.cpp file)
{
bool row_contains_word(vector<char> const& row, string word);
bool col_contains_word(/*vector<char> const& row,*/ string word); // this needs more work, since the col is a little different
}
}
This can hide the implementation from the searching from the interface of deciding if a word is contained in the board.

Related

C++ runtime error: addition of unsigned offset?

I wrote the following to check if text is palindrome, I run it on leetcode and I am getting errors:
class Solution {
public:
bool isPalindrome(string s) {
int l=0,r=s.length()-1;
while(l<r)
{
while (!isalpha(s[r]))
{
--r;
}
while (!isalpha(s[l]))
{
++l;
}
if (tolower(s[r])!=tolower(s[l]))
return false;
--r;
++l;
}
return true;
}
};
Line 1061: Char 9: runtime error: addition of unsigned offset to
0x7ffc7cc10880 overflowed to 0x7ffc7cc1087f (basic_string.h) SUMMARY:
UndefinedBehaviorSanitizer: undefined-behavior
/usr/bin/../lib/gcc/x86_64-linux-gnu/9/../../../../include/c++/9/bits/basic_string.h:1070:9
what's the problem with my code?
You're going out of bounds here:
while (!isalpha(s[r]))
and here
while (!isalpha(s[l]))
r can became negative and l can become >= s.length().
You should add some checks like
while (l < r && !isalpha(s[r]))
and
while (l < r && !isalpha(s[l]))
The same problem in this line
if (tolower(s[r])!=tolower(s[l]))
This should be
if (l < r && tolower(s[r])!=tolower(s[l]))
Different approach (C++20)
A different approach is to erase all non-alpha characters from s with
std::erase_if(s, [](char c) { return !isalpha(c); });
and remove the inner while loops.
I think you were very close to the solution. The pitfall here are that:
you are modifying the loop control variable more than once in the loop
(as consequence) you are using the loop control variable after changing their values without further checks.
The easy way to fix this kind of issue is to do one single action for every iteration. you can achieve this just using "else".
class Solution {
public:
bool isPalindrome(string s) {
int l=0,r=s.length()-1;
while(l<r)
{
if(!isalpha(s[r]))
{
--r;
}
else if(!isalpha(s[l]))
{
++l;
}
else if (tolower(s[r])!=tolower(s[l]))
{
return false;
}
else
{
--r;
++l;
}
}
return true;
}
};

Palindrome Partitioning (how to figure out how to use DFS)

My general question is how to figure out how to use DFS. It seems to be a weak part of my knowledge. I have vague idea but often get stuck when the problem changes. It caused a lot of confusion for me.
For this question, I got stuck with how to write DFS with recursion.
Given a string s, partition s such that every substring of the partition is a palindrome.
Return all possible palindrome partitioning of s.
For example, given s = "aab",
Return
[
["aa","b"],
["a","a","b"]
]
My first attempt was stuck in the loop of the helper function. Then from searching on internet, I found that bool palindrome(string s) can be written as a different signature.
bool palindrome(string &s, int start, int end)
This leads to the correct solution.
Here's the code of my initial attempt:
class Solution {
public:
bool palindrome(string s)
{
int len = s.size();
for (int i=0;i<len/2; i++)
{
if (s[i]!=s[len-i])
return false;
}
return true;
}
void helper( int i, string s, vector<string> &p, vector<vector<string>> &ret)
{
int slen = s.size();
if (i==slen-1&&flag)
{
ret.push_back(p);
}
for (int k=i; k<slen; k++)
{
if (palindrome(s.substr(0,k)))
{
p.push_back(s.substr(0,k)); //Got stuck
}
}
i++;
}
vector<vector<string>> partition(string s) {
vector<vector<string>> ret;
int len=s.size();
if (len==0) return ret;
vector<string> p;
helper(0,s,p,ret);
return ret;
}
};
Correct one:
class Solution {
public:
bool palindrome(string &s, int start, int end)
{
while(start<end)
{
if (s[start]!=s[end])
return false;
start++;
end--;
}
return true;
}
void helper( int start, string &s, vector<string> &p, vector<vector<string>> &ret)
{
int slen = s.size();
if (start==slen)
{
ret.push_back(p);
return;
}
for (int i=start; i<s.size(); i++)
{
if (palindrome(s, start, i))
{
p.push_back(s.substr(start,i-start+1));
helper(i+1,s,p,ret);
p.pop_back();
}
}
}
vector<vector<string>> partition(string s) {
vector<vector<string>> ret;
int len=s.size();
if (len==0) return ret;
vector<string> p;
helper(0,s,p,ret);
return ret;
}
};
Edit Dec. 4, 2014: I saw some approach using dynamical programming but can't understand the code completely.
esp. isPalin[i][j] = (s[i] == s[j]) && ((j - i < 2) || isPalin[i+1][j-1]);
Why j-I<2 instead of j-I<1?
class Solution {
public:
vector<vector<string>> partition(string s) {
int len = s.size();
vector<vector<string>> subPalins[len+1];
subPalins[0] = vector<vector<string>>();
subPalins[0].push_back(vector<string>());
bool isPalin[len][len];
for (int i=len-1; i>=0; i--)
{
for (int j=i; j<len; j++)
{
isPalin[i][j] = (s[i]==s[j])&&((j-i<2)||isPalin[i+1][j-1]);
}
}
for (int i=1; i<=len;i++)
{
subPalins[i]=vector<vector<string>>();
for (int j=0; j<i; j++)
{
string rightStr=s.substr(j,i-j);
if (isPalin[j][i-1])
{
vector<vector<string>> prepar=subPalins[j];
for (int t=0; t<prepar.size(); t++)
{
prepar[t].push_back(rightStr);
subPalins[i].push_back(prepar[t]);
}
}
}
}
return subPalins[len];
}
};
What exactly are you asking? You have correct working code and your non-working code which is not that different.
I guess I can point out several issues with your code - may be it will be helpful to you:
in the palindrome() function you should compare s[i] to s[len-1-i] rather than to just s[len-i] in the if, since in former case you will compare 1st element (having index 0) to the non-existent element (index len). That might be the reason helper() got stuck.
in the helper() function flag is not initialized. In the for cycle, the end condition should be k<slen-1 instead of k<slen, since in latter case you will omit checking the substring that includes the terminal symbol of the string. Also, incrementing i in the end of helper() is pointless. Finally, indentations are messy in the helper() function.
Not sure why you use DFS - what is the meaning of your graph, what are the vertices and edges here? As to how the recursion works here: in the helper() function you start checking substrings of increased length for being palindrome. If the palindrome is found, you place it into p vector (which represent your current partitioning) and try to break the remainder of the string into palindromes by calling helper() recursively. If you succeed in that (i.e. if the whole string is completely partitioned into palindromes) you place the contents of p vector (current partitioning) into ret (set of all found partitionings), and then clear p to prepare it for the analysis of the next partition (purge of p is achieved by pop_back() call that follows recursive call of helper()). If, on the other hand, you fail to completely break string into palindromes, the p is purged as well, but without transferring its content into ret (this is due to the fact that recursive call for the last piece of string - which is not a palindrome - returns without calling helper() for the final symbol and thus pushing p into ret does not occur). Therefore you end up having all possible palindrome partitionings in the ret.
Hi~ this is my code using DFS + backtracking.
class Solution
{
public:
bool isPalindrome (string s) {
int i = 0, j = s.length() - 1;
while(i <= j && s[i] == s[j]) {
i++;
j--;
}
return (j < i);
}
void my_partition(string s, vector<vector<string> > &final_result, vector<string> &every_result ) {
if (s.length() ==0)
final_result.push_back(every_result);
for (int i =1; i <= s.length();++i) {
string left = s.substr(0,i);
string right = s.substr(i);
if (isPalindrome(left)) {
every_result.push_back(left);
my_partition(right, final_result, every_result);
every_result.pop_back();
}
}
}
vector<vector<string>> partition(string s) {
vector<vector<string> > final_result;
vector<string> every_result;
my_partition(s, final_result, every_result);
return final_result;
}
};
I have done Palindrome Partitioning using backtracking. Depth-first search was used here, idea is to split the given string so that the prefix is a palindrome. push prefix in a vector now explore the string leaving that prefix and then finally pop the last inserted element,
Well on spending time on backtracking is of the form, choose the element, explore without it and unchoose it.
enter code here
#include<iostream>
#include<vector>
#include<string>
using namespace std;
bool ispalidrome(string x ,int start ,int end){
while(end>=start){
if(x[end]!=x[start]){
return false;
}
start++;
end--;
}
return true;
}
void sub_palidrome(string A,int size,int start,vector<string>&small, vector < vector < string > >&big ){
if(start==size){
big.push_back(small);
return;
}
for(int i=start;i<size;i++){
if( ispalidrome(A,start,i) ){
small.push_back(A.substr(start,i-start+1));
sub_palidrome(A,size,i+1,small,big);
small.pop_back();
}
}
}
vector<vector<string> > partition(string A) {
int size=A.length();
int start=0;
vector <string>small;
vector < vector < string > >big;
sub_palidrome(A,size,start,small,big);
return big;
}
int main(){
vector<vector<string> > sol= partition("aab");
for(int i=0;i<sol.size();i++){
for(int j=0;j<sol[i].size();j++){
cout<<sol[i][j]<<" ";
}
cout<<endl;
}
}

push attribute data to trie, add to multiple keys

my knowledge is limited but I have been working (hacking) at this specific data structure for awhile
I use a trie to store ontology strings that are then returned as a stack including the 'gap' proximity when get (string) is called. As an add on the trie stores attributes on the key. The further down the string the greater the detail of the attribute. This is working well for my purposes.
As an additional add on, I use a wildcard to apply an attribute to all child nodes. For example, to add 'paws' to all subnodes of 'mammals.dogs.' I push(mammals.dogs.*.paws). Now, all dogs have paws.
The problem is only the first dog get paws. The function works for push attributes without wild
If you want I can clean this up and give a simplified version, but in the past i've found on stackoverflow it is better to just give the code; I use 'z' as the '*' wild
void Trie::push(ParseT & packet)
{
if (root==NULL) AddFirstNode(); // condition 1: no nodes exist, should this be in wrapper
const string codeSoFar=packet.ID;
AddRecord(root, packet, codeSoFar); //condotion 2: nodes exist
}
void Trie::AddFirstNode(){ // run-once, initial condition of first node
nodeT *tempNode=new nodeT;
tempNode->attributes.planType=0;
tempNode->attributes.begin = 0;
tempNode->attributes.end = 0;
tempNode->attributes.alt_end = 0;
root=tempNode;
}
//add record to trie with mutal recursion through InsertNode
//record is entered to trie one char at a time, char is removed
//from record and function repeats until record is Null
void Trie::AddRecord(nodeT *w, ParseT &packet, string codeSoFar)
{
if (codeSoFar.empty()) {
//copy predecessor vector at level n, overwrites higher level vectors
if (!packet.predecessorTemp.empty())
w->attributes.predecessorTemp = packet.predecessorTemp;
return; //condition 0: record's last char
}
else { //keep parsing down record path
for (unsigned int i = 0; i < w->alpha.size(); i++) {
if (codeSoFar[0] == w->alpha[i].token_char || codeSoFar[0] == 'z') {
return AddRecord(w->alpha[i].next, packet, codeSoFar.substr(1)); // condition 2: char exists
}
}
InsertNode(w, packet, codeSoFar); //condition 3: no existing char --> mutal recursion
}
}
//AddRecord() helper function
void Trie::InsertNode(nodeT *w, ParseT &packet, string codeSoFar) // add new char to vector array
{
for (unsigned int i=0; i <=w->alpha.size(); i++) { // loop and insert tokens in sorted vector
if (i==w->alpha.size() || codeSoFar[0] < w->alpha[i].token_char) { //look for end of vector or indexical position
//create new TokenT
tokenT *tempChar=new tokenT;
tempChar->next=NULL;
tempChar->token_char=codeSoFar[0];
//create new nodeT
nodeT *tempLeaf=new nodeT;
tempLeaf->attributes.begin = 0;
tempLeaf->attributes.end = 0;
tempLeaf->attributes.planType = 0;
tempLeaf->attributes.alt_end = 0;
//last node
if (codeSoFar.size() == 1){
tempLeaf->attributes.predecessorTemp = packet.predecessorTemp;
}
//link TokenT with its nodeT
tempChar->next=tempLeaf;
AddRecord(tempLeaf, packet, codeSoFar.substr(1)); //mutual recursion --> add next char in record, if last char AddRecord will terminate
w->alpha.insert(w->alpha.begin()+i, *tempChar);
return;
}
}
}
root is global nodeT *w
struct ParseT {
string ID; //XML key
int begin = 0; //planned or actual start date
int end = 0; //planned or actual end date - if end is empty then assumed started but not compelted and flag with 9999 and
int alt_end = 0; //in case of started without completion 9999 case, then this holds expected end
int planType = 0; //actuals == 1, forecast == 2, planned == 3
map<string, string> aux;
vector<string> resourceTemp;
vector<string> predecessorTemp;
};
In this code
for (unsigned int i = 0; i < w->alpha.size(); i++) {
if (codeSoFar[0] == w->alpha[i].token_char || codeSoFar[0] == 'z') {
return AddRecord(w->alpha[i].next, packet, codeSoFar.substr(1)); // condition 2: char exists
}
}
you are returning as soon as you call AddRecord, even if it is because of a wildcard. It might be easier to have a separate loop when codeSoFar[0] == 'z' that goes through all the alphas and adds the record. Then have an else clause that does your current code.
Edit: Here is what I meant, in code form:
else { //keep parsing down record path
// Handle wildcards
if (codeSoFar[0] == 'z') {
for (unsigned int i = 0; i < w->alpha.size(); i++) {
AddRecord(w->alpha[i].next, packet, codeSoFar.substr(1)); // condition 2: char exists
}
}
else {
// Not a wildcard, look for a match
for (unsigned int i = 0; i < w->alpha.size(); i++) {
if (codeSoFar[0] == w->alpha[i].token_char) {
return AddRecord(w->alpha[i].next, packet, codeSoFar.substr(1)); // condition 2: char exists
}
}
InsertNode(w, packet, codeSoFar); //condition 3: no existing char --> mutal recursion
}
}

if() skipping my variable check

I have following code:
std::vector<std::string> GetSameID(std::vector<string>& allFiles, int id) {
std::vector<std::string> returnVector;
for(std::vector<string>::iterator it = allFiles.begin(); it != allFiles.end(); ++it) {
if(GetID(*it) == id) {
int index = (*it).find("_CH2.raw");
if(index > 0) {
continue; //this works
}
if(0 < ((*it).find("_CH2.raw"))) {
continue; //this doesn't
}
string ext = PathFindExtension((*it).c_str());
if(ext == ".raw") {
returnVector.push_back(*it);
}
}
}
return returnVector;
}
My issue is, why is the if(0 < ((*it).find("_CH2.raw"))) not working that way? My files are named
ID_0_X_0_Y_128_CH1.raw
ID_0_X_0_Y_128_CH2.raw
(different ID, X and Y, for Channel 1 and Channel 2 on the oscilloscope).
When I do it the long way around (assign index, and then check index), it works, I don't understand though why the short version, which is more readable imo, is not working.
According to http://en.cppreference.com/w/cpp/string/basic_string/find, string::find() returns a size_t -- which is an unsigned type -- so it can never be less-than zero.
When it doesn't find something, it returns string::npos, which is also an unsigned type, but when you shove it into an int (implicitly converting it) it becomes a negative value -- this is why your first set of code works.

prune recursive search paths

my knowledge is limited, writing in C++ for 2 months
In this function string code is recursively decrements chars until the base case "" is found. I want to prune some paths before the base case is found, and for some string code a path to the base case will not be found. For the prune I want to compare an attribute in the path with parameter int time. This searches a trie made of 'nodeT'
struct charT {
char letter;
nodeT *next;
};
struct nodeT {
bool isOperation;
bool isCode;
int time;
Vector<charT> alpha;
};
nodeT *root
usage:
string code = "12345";
int time = convertToEpoch(20120815); //my epoch function
containsCode(code, time)
bool containsCode(string code, int time)
{
if(root == NULL) return false;
else return containsCodeHelper(root, code, time);
}
bool containsCodeHelper(nodeT *w, string code, int time)
{
if(code == "") //base case: all char found
return w->isCode;
else {
if (w->isOperation && w->time != time) return false; //case 2: time check OK <- at a midpoint in the path
for(int i = 0; i < w->alpha.size(); i++) { //Loop through the leaf
if (w->alpha[i].letter == code[0]) //case 3: leaf exists
return containsCodeHelper(w->alpha[i].next, code.substr(1), time);
}
}
return false; //if no path
}
This function worked well before adding the time check prune, it now loops, returns false if outside time but then starts again with the candidate string code from char location 0.
Questions: 1) Is a nested return false kicking the recursion back to the next call for loop, 2) should the time prune be placed in the for loop with a logical return false or return 'path', 3) is this more fundamentally messed-up and I need to learn a C++ concept <- please explain if yes.
Also, the posted function is a simplified version of the actual function - there is a modifier to time and a 'step over' path that I left out. In past question I found that these 'addons' distract from the question.
after some reworking it now functions fine - possibly it always did; I changed to read attribute return w->isCode to return true, that seemed to be the biggest issue - I will debug the trie constructor and see if it is setting the attribute at the end of each path.
boolcontainsCodeHelper(nodeT *w, string code, int time)
{
if(code == "") //base case: all char found
return true;
else {
if ( w->isOperation && (!((w->begin-wag) <= time && time <= (w->end+wag) ) && time != 9999 ) )
return false; //case 2: time
else {
for(int i = 0; i < w->alpha.size(); i++) { //Loop through all of the children of the current node
if (w->alpha[i].letter == word[0])
return containsCodeHelper(w->alpha[i].next, word.substr(1), time, wag);
else if (word[0] == 'ΕΎ') //step over '0' all subnodes
if (containsCodeHelper(w->alpha[i].next, word.substr(1), time, wag))
return true;
}
}
}
return false; //if char is missing - meaning the exact code is not there - terminates garbage subnode paths
}
I don't see any difference between having return false; at the end and leaving it out. Also still confused as why the special case needs if( bool fn()) return true; rather than just return ( bool fn()); i found that solution through trial and error with help from another stack overflow thread