Palindrome Partitioning (how to figure out how to use DFS) - c++

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;
}
}

Related

Sub array with a given sum

(The given problem is referred from : https://practice.geeksforgeeks.org/problems/subarray-with-given-sum-1587115621/1?page=1&difficulty[]=0&curated[]=1&sortBy=submissions )
I tried a solution to the above problem but it failed for large values for N and S.
I attempted the following solution to the above problem:
class Solution
{
public:
//Function to find a continuous sub-array which adds up to a given number.
vector<int> subarraySum(int arr[], int n, long long s)
{
// Your code here
long long sum=arr[0];
int first=0,last=0;
vector<int> v{-1,-1};
vector<int> a{-1};
if(arr[0]==s)
{
v[0]=1;
v[1]=1;
return v;
}
for(int i=0;first<n && last<n;i++){
if(sum>s){
if(first==last && (last+1<n)){
sum=sum-arr[first]+arr[first+1];
first++;
last++;
}
else{
if(first==last && last==n-1){
break;
}
if(first<last)
{ sum=sum-arr[first];
first++;
}
} else if(sum<s){
last++;
sum+=arr[last];
}
else if(sum==s){
v[0]=first+1;
v[1]=last+1;
return v;
}
}
return a;
}
};
In the code, 'first' and 'last' are the indexed of the first and the last element of the sub-array.
(Edit: The edited code works!, thanks for all the help!)

Getting wrong answer in a DP problem although implementation looks correct

I was trying to solve Reduce String on codechef which says
Give a string s of length l, and a set S of n sample string(s). We do reduce the string s using the set S by this way:
Wherever Si appears as a consecutive substring of the string s, you can delete (or not) it.
After each deletion, you will get a new string s by joining the part to the left and to the right of the deleted substring.
I wrote a recursive function as follows:-
Basically what i am doing in my code is either don't delete the character or delete it if it is part of any substring but it is giving wrong answer.
#include <bits/stdc++.h>
using namespace std;
#define mx 255
int dp[mx];
unordered_map<string,int> sol;
void init(int n)
{
for(int i=0;i<n;i++)
{
dp[i]=-1;
}
}
int solve(string str,int low,int high,vector<string> smp)
{
if(low>high)
{
return 0;
}
if(dp[low]!=-1)
{
return dp[low];
}
int ans=1+solve(str,low+1,high,smp);
for(int i=low;i<high;i++)
{
string tem=str.substr(low,i-low+1);
for(int j=0;j<smp.size();j++)
{
cout<<"low i high str"<<low<<" "<<i<<" "<<high<<" "<<smp[j]<<" "<<tem<<endl;
if(tem.compare(smp[j])==0)
{
ans=min(ans,solve(str,i+1,high,smp));
}
}
}
return dp[low]=ans;
}
signed main()
{
sol.clear();
string str;
vector<string> smp;
int n;
cin>>str;
cin>>n;
for(int i=0;i<n;i++)
{
string tem;
cin>>tem;
smp.push_back(tem);
}
int len=str.length();
init(len+1);
cout<<solve(str,0,len-1,smp)<<endl;
return 0;
}
PS:
link to the question
This question is toughest(seen so far) and most beautiful(again seen so far) question based on DP ON INTERVALS.
The initial code would definitely not work since it only considers single pass on the string and would not consider remaining string after deleting the patterns again and again.
There are 3 cases:-
Case 1 Either character is not deleted.
Case 2It is deleted as a part of contiguous substring.
Case 3It is deleted as a part of subsequence that matches any word given in the set of patterns and everything that is not part of that subsequence is deleted first as a substring(which again belongs to set of words).
The third part is the most tricky and requires enough thinking and is even tougher to implement too.
So for every substring we need to check whether this substring can be completely destroyed or not.
The function compute_full_recur() is the function that ensures that whether substring can be deleted either in Case 2 or Case 3.
The function compute_full takes care of Case 1.And finally this code will not run on codechef link since all the function are recursive with memoization but to verify the code is working i Have run it on Problem Reducto of Hackerrank which is exact similar with lower constraints.Download test cases and then run on test cases on your PC for verifying.
#include <iostream>
#include <vector>
#include <string>
using namespace std;
#define mx 252
#define nx 40
bool full[mx][mx],vis[mx][mx],full_recur[mx][mx][nx][nx];
int ans[mx];
void init()
{
for(int i=0;i<mx;i++)
{
for(int j=0;j<mx;j++)
{
full[i][j]=false,vis[i][j]=false;
}
}
for(int i=0;i<mx;i++)
{
ans[i]=-1;
}
for(int i=0;i<mx;i++)
{
for(int j=0;j<mx;j++)
{
for(int k=0;k<nx;k++)
{
for(int l=0;l<nx;l++)
{
full_recur[i][j][k][l]=false;
}
}
}
}
}
bool compute_full_recur(string str,int low,int high,vector<string> pat,int idx,int len)
{
if(low>high&&len==pat[idx].length())
{
return true;
}
if(low>high&&len<pat[idx].length())
{
full_recur[low][high][idx][len]=false;
return false;
}
if(str[low]==pat[idx][len]&&compute_full_recur(str,low+1,high,pat,idx,len+1))
{
return full_recur[low][high][idx][len]=true;
}
for(int i=low+1;i<=high;i++)
{
if(str[low]==pat[idx][len]&&full[low+1][i]&&compute_full_recur(str,i+1,high,pat,idx,len+1))
{
return full_recur[low][high][idx][len]=true;
}
}
full_recur[low][high][idx][len]=false;
return false;
}
void compute_full(string str,int low,int high,vector<string> pats)
{
if(low>high)
{
return;
}
if(vis[low][high])
{
return;
}
vis[low][high]=true;
compute_full(str,low+1,high,pats);
compute_full(str,low,high-1,pats);
for(int i=0;i<pats.size();i++)
{
if(!full[low][high])
full[low][high]=compute_full_recur(str,low,high,pats,i,0);
}
}
int compute_ans(string str,int low,int high)
{
if(low>high)
{
return 0;
}
if(ans[low]!=-1)
{
return ans[low];
}
int sol=1+compute_ans(str,low+1,high);
for(int i=low+1;i<=high;i++)
{
if(full[low][i]==true)
{
sol=min(sol,compute_ans(str,i+1,high));
}
}
return ans[low]=sol;
}
signed main()
{
int t;
cin>>t;
while(t--)
{
string str;
int n;
vector<string> pats;
cin>>n>>str;
for(int i=0;i<n;i++)
{
string tem;
cin>>tem;
pats.push_back(tem);
}
init();
compute_full(str,0,str.length()-1,pats);
cout<<compute_ans(str,0,str.length()-1)<<endl;
}
return 0;
}

C++ QuickSort not sorting

void quickSort(vector<double> unsortedData, int leftBound, int rightBound) {
if (leftBound < rightBound) {
double i = partitionStep(unsortedData, leftBound, rightBound);
quickSort(unsortedData, leftBound, i-1);
quickSort(unsortedData, i + 1, rightBound);
}
}
double partitionStep(vector<double> unsortedData, int leftBound, int rightBound) {
double pivot = unsortedData[rightBound];
while (leftBound <= rightBound) {
while ((unsortedData[leftBound] < pivot)) {
leftBound++;
}
while ((unsortedData[rightBound] > pivot)) {
rightBound--;
}
if (unsortedData[leftBound] == unsortedData[rightBound]) {
leftBound++;
} else if (leftBound < rightBound) {
double temp = unsortedData[leftBound];
unsortedData[leftBound] = unsortedData[rightBound];
unsortedData[rightBound] = temp;
}
}
return rightBound;
}
I need to sort a vector of doubles. This code runs but the vector is not sorted at the end. It's probably something that I am overlooking. Thoughts?
A high level description of your quickSort routine is:
Take as input a copy of the original vector and the endpoints of a range
Do stuff with the copy
Discard the copy
which isn't particularly useful. Change the input argument to vector<double>& so that you're doing stuff with a reference to the original vector rather than a copy.

What's the correct approach to solve SPOJ www.spoj.com/problems/PRHYME/?

I have been trying to solve this problem SPOJ www.spoj.com/problems/PRHYME/? for several days, but have had no success.
Here is the problem in brief:
Given is a wordlist L, and a word w. Your task is to find a word in L that forms a perfect rhyme with w. This word u is uniquely determined by these properties:
It is in L.
It is different from w.
Their common suffix is as long as possible.
Out of all words that satisfy the previous points, u is the lexicographically smallest one.
Length of a word will be<=30.
And number of words both in the dictionary and the queries can be 2,50,000.
I am using a trie to store all the words in the dictionary reversed.
Then to solve the queries I proceed in the following fashion:-
If word is present in the trie,delete it from trie.
Now traverse the trie from the root till the point the character from the query string match the trie values.Let this point where last character match was found be P.
Now from this point P onward ,I traverse the trie using DFS,and on encountering a leaf node,push the string formed to the possible results list.
Now I return the lexicographic ally smallest result from this list.
When I submit my solution on SPOJ,my solution gets a Time Limit Exceeded Error.
Can someone please suggest a detailed algorithm or hint to solve this problem ?
I can post my code if required.
#include<iostream>
#include<cstdio>
#include<cstring>
#include<climits>
#include<vector>
#include<string>
#include<algorithm>
#include<cctype>
#include<cstdlib>
#include<utility>
#include<map>
#include<queue>
#include<set>
#define ll long long signed int
#define ull unsigned long long int
const int alpha=26;
using namespace std;
struct node
{
int value;
node * child[alpha];
};
node * newnode()
{
node * newt=new node;
newt->value=0;
for(int i=0;i<alpha;i++)
{
newt->child[i]=NULL;
}
return newt;
}
struct trie
{
node * root;
int count;
trie()
{
count=0;
root=newnode();
}
};
trie * dict=new trie;
string reverse(string s)
{
int l=s.length();
string rev=s;
for(int i=0;i<l;i++)
{
int j=l-1-i;
rev[j]=s[i];
}
return rev;
}
void insert(string s)
{
int l=s.length();
node * ptr=dict->root;
dict->count++;
for(int i=0;i<l;i++)
{
int index=s[i]-'a';
if(ptr->child[index]==NULL)
{
ptr->child[index]=newnode();
}
ptr=ptr->child[index];
}
ptr->value=dict->count;
}
void dfs1(node *ptr,string p)
{
if(ptr==NULL) return;
if(ptr->value) cout<<"word" <<p<<endl;
for(int i=0;i<26;i++)
{
if(ptr->child[i]!=NULL)
dfs1(ptr->child[i],p+char('a'+i));
}
}
vector<string> results;
pair<node *,string> search(string s)
{
int l=s.length();
node * ptr=dict->root;
node *save=ptr;
string match="";
int i=0;
bool no_match=false;
while(i<l and !no_match)
{
int in=s[i]-'a';
if(ptr->child[in]==NULL)
{
save=ptr;
no_match=true;
}
else
{
ptr=ptr->child[in];
save=ptr;
match+=in+'a';
}
i++;
}
//cout<<s<<" matched till here"<<match <<" "<<endl;
return make_pair(save,match);
}
bool find(string s)
{
int l=s.length();
node * ptr=dict->root;
string match="";
for(int i=0;i<l;i++)
{
int in=s[i]-'a';
//cout<<match<<"match"<<endl;
if(ptr->child[in]==NULL)
{
return false;
}
ptr=ptr->child[in];
match+=char(in+'a');
}
//cout<<match<<"match"<<endl;
return true;
}
bool leafNode(node *pNode)
{
return (pNode->value != 0);
}
bool isItFreeNode(node *pNode)
{
int i;
for(i = 0; i < alpha; i++)
{
if( pNode->child[i] )
return false;
}
return true;
}
bool deleteHelper(node *pNode, string key, int level, int len)
{
if( pNode )
{
// Base case
if( level == len )
{
if( pNode->value )
{
// Unmark leaf node
pNode->value = 0;
// If empty, node to be deleted
if( isItFreeNode(pNode) )
{
return true;
}
return false;
}
}
else // Recursive case
{
int index = (key[level])-'a';
if( deleteHelper(pNode->child[index], key, level+1, len) )
{
// last node marked, delete it
free(pNode->child[index]);
pNode->child[index]=NULL;
// recursively climb up, and delete eligible nodes
return ( !leafNode(pNode) && isItFreeNode(pNode) );
}
}
}
return false;
}
void deleteKey(string key)
{
int len = key.length();
if( len > 0 )
{
deleteHelper(dict->root, key, 0, len);
}
}
string result="***";
void dfs(node *ptr,string p)
{
if(ptr==NULL) return;
if(ptr->value )
{
if((result)=="***")
{
result=reverse(p);
}
else
{
result=min(result,reverse(p));
}
}
for(int i=0;i<26;i++)
{
if(ptr->child[i]!=NULL)
dfs(ptr->child[i],p+char('a'+i));
}
}
int main(int argc ,char ** argv)
{
#ifndef ONLINE_JUDGE
freopen("prhyme.in","r",stdin);
#endif
string s;
while(getline(cin,s,'\n'))
{
if(s[0]<'a' and s[0]>'z')
break;
int l=s.length();
if(l==0) break;
string rev;//=new char[l+1];
rev=reverse(s);
insert(rev);
//cout<<"...........traverse..........."<<endl;
//dfs(dict->root);
//cout<<"..............traverse end.............."<<endl;
}
while(getline(cin,s))
{
results.clear();
//cout<<s<<endl;
int l=s.length();
if(!l) break;
string rev;//=new char[l+1];
rev=reverse(s);
//cout<<rev<<endl;
bool del=false;
if(find(rev))
{
del=true;
//cout<<"here found"<<endl;
deleteKey(rev);
}
if(find(rev))
{
del=true;
//cout<<"here found"<<endl;
deleteKey(rev);
}
else
{
//cout<<"not here found"<<endl;
}
// cout<<"...........traverse..........."<<endl;
//dfs1(dict->root,"");
// cout<<"..............traverse end.............."<<endl;
pair<node *,string> pp=search(rev);
result="***";
dfs(pp.first,pp.second);
//cout<<"search results"<<endl;
//dfs1(pp.first,pp.second);
//cout<<"end of search results"<<
for(int i=0;i<results.size();i++)
{
results[i]=reverse(results[i]);
// cout<<s<<" "<<results[i]<<endl;
}
string smin=result;
if(del)
{
insert(rev);
}
cout<<smin<<endl;
}
return 0;
}
Your algorithm (using a trie that stores all reversed words) is a good start. But one issue with it is that for each lookup, you have to enumerate all words with a certain suffix in order to find the lexicographically smallest one. For some cases, this can be a lot of work.
One way to fix this: In each node (corresponding to each suffix), store the two lexicographically smallest words that have that suffix. This is easy to maintain while building the trie by updating all ancestor nodes of each newly added leaf (see pseudo-code below).
Then to perform a lookup of a word w, start at the node corresponding to the word, and go up in the tree until you reach a node which contains a descendant word other than w. Then return the lexicographically smallest word stored in that node, or the second smallest in case the smallest is equal to w.
To create the trie, the following pseudo-code can be used:
for each word:
add word to trie
let n be the node corresponding to the new word.
for each ancestor a of n (including n):
if a.smallest==null or word < a.smallest:
a.second_smallest = a.smallest
a.smallest = word
else if a.second_smallest==null or word < a.second_smallest:
a.second_smallest = word
To lookup a word w:
let n be the node corresponding to longest possible suffix of w.
while ((n.smallest==w || n.smallest==null) &&
(n.second_smallest==w || n.second_smallest==null)):
n = n.parent
if n.smallest==w:
return n.second_smallest
else:
return n.smallest
Another similar possibility is to use a hash table mapping all suffixes to the two lexicographically smallest words instead of using a trie. This is probably easier to implement if you can use std::unordered_map.

Hash Table of Vectors of Person Objects c

I've been working on a very in depth project for one of my classes. It supposed to read in Person objects and put them into a hash table. I'm still trying to get my head around the concept of a hash table so any help would be appreciated.
It will be hashing based on last name and since some people may have the same last name, I was going to make each bucket a vector of Person objects. I'm trying to test the class by adding a person to the hash function and then returning it. My code compiles successfully but I get a thread error in the put function on this line: table[index].push_back(p);
Could anyone please help me figure out what is going wrong? Thank you!
int main()
{
HashTable ht(10);
ht.put(p1, p1->lName);
ht.getName("Booras");
}
HashTable:
#include "Person.h"
#include <vector>
class HashTable: public DataStructures
{
private:
vector<vector<Person>> table;
public:
HashTable(int tableSize);
~HashTable();
int tableSize;
void getName(string str); //prints out friends with matching name
void put(Person p, string str);
void remove(Person *p, string str);
int hash(string str);
};
HashTable::HashTable(int tableSize)
{
vector< vector<Person> > table(tableSize, vector<Person>(tableSize));
for (int i = 0; i < tableSize; i++) {
table.push_back(vector<Person>()); // Add an empty row
}
}
HashTable::~HashTable()
{
}
//Find a person with the given last name
void HashTable::getName(string key)
{
int index = hash(key);
for(int i=0; i<table[index].size(); i++)
{
if(table[index][i].lName.compare(key) == 0)
std::cout << "Bucket: " << index << "Bin: " << i;
table[index][i].print();
}
//create exception for person not found
}
void HashTable::put(Person p, string str)
{
int index = hash(str);
table[index].push_back(p);
}
void HashTable::remove(Person *p, string str)
{
int index = hash(str);
int i=0;
while(&table[index][i] != p && i<table[index].size())
i++;
for(int j=i; j<table[index].size()-1; j++)
table[index][j] = table[index][j+1];
table[index].pop_back();
}
int HashTable::hash(string str)
{
int hashValue = 0;
for(int i=0; i<str.length(); i++)
{
hashValue = hashValue + int(str[i]);
}
hashValue %= tableSize;
if(hashValue<0) hashValue += tableSize;
return hashValue;
}
Main:
int main() {
Person *p1 = new Person("Kristy", "Booras", "Reston", "03/15");
HashTable ht(10);
ht.put(*p1, p1->lName);
ht.get("Booras");
return 0;
}
You don't show us the HashTable::hash(string) member function, but I'd assume that your problems originate in the HashTableconstructor: You don't initialize the tableSize member variable, which you'll need to calculate a valid hashed index.
While looking at the constructor:
HashTable::HashTable(int tableSize)
{
vector< vector<Person> > table(tableSize, vector<Person>(tableSize));
This has initialized table to have tableSize non-empty elements, for a total of tableSize * tableSizedefault-constructed Person objects.
for (int i = 0; i < tableSize; i++) {
table.push_back(vector<Person>()); // Add an empty row
}
}
Now you have added more rows, so that table.size() == 2*tableSize, with the first half of entries non-empty (as explained above) and the second half holding empty vectors.
That is probably not what you intended.
And in all of that you haven't initialized the member tableSize. It easily gets confusing, if you use local variables or argument names that hide member names.