I am working on palindrome detector in C++ that reads in a file and marks the lines that are palindromes with the indicator "*". Here's what I have.
PalindromeDetector::PalindromeDetector(const string& iFile, const string& oFile) {
myInFile = iFile;
myOutFile = oFile;
}
void PalindromeDetector::detectPalindrome() {
ifstream fin(myInFile.data());
ofstream fout(myOutFile.data());
string nLine, palLine;
while (getline(fin, nLine)){
if (isPalindrome(nLine)){
fout << nLine << " ***";
} else {
fout << nLine;
}
}
fin.close();
fout.close();
}
bool PalindromeDetector::isPalindrome(const string& str) {
Stack<char> charStack(1);
ArrayQueue<char> charQueue(1);
char ch1, ch2;
for ( unsigned i = 0; i < str.size(); i++){
if (isalnum (str[i])){
tolower(str[i]);
try {
charStack.push(str[i]);
charQueue.append(str[i]);
} catch ( StackException& se ){
charStack.setCapacity(charStack.getCapacity() * 2);
charQueue.setCapacity(charQueue.getCapacity() * 2);
charStack.push(str[i]);
charQueue.append(str[i]);
}
} else {
while ( !charStack.isEmpty() || !charQueue.isEmpty() ){
ch1 = charStack.pop();
ch2 = charQueue.remove();
if ( ch1 != ch2 ){
return false;
}
}
}
}
return true;
}
I'm having 2 problems with this so far:
1. It isn't correctly outputting the file with the "*" at the end of the line; it's doing it at the front for some reason.
2. It only marks the first line in each block of the file, not the lines that are palindromes.
I would really appreciate some help on this.
Why are you making isPalidrome so complicated?
Could be done as thus
bool isPalidrome(const string &s) {
int left = 0;
int right = s.length() - 1;
while (left < right) {
if (s[left] != s[right]) return false;
left++; right--;
}
return true;
}
Of course you might what to add case-insensitivity
EDIT
Using the more silly way of stacks and queues
bool isPalidrome(const string &s) {
// Put everything on both
std::stack<char> lifo;
std::queue<char> fifo;
unsigned int loop;
for (loop = 0; loop < s.length); ++loop) {
lifo.push(s[loop]);
fifo.push(s[loop]);
}
// Note stack and queue the characters are in reverse order from each other
for (loop = 0; loop < s.length); ++loop) {
if (lifo.pop() != fifo.pop()) return false;
}
return true;
}
Related
I am stuck with the error "string subscript out of range".
After testing, I am pretty sure that it's because of this function, which is used for reading values in a file, but have no idea what's wrong:
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
string read(string value) {
ifstream input;
string line="", output="";
size_t pos;
bool a = true;
int i = 0;
input.open("pg_options.txt");
if (!input.is_open()) {
cout << "pg_options.txt missing.";
return "error";
}
while (getline(input, line)) { //get file lines
pos = line.find(value);
if (pos == string::npos) { //if value in line
while (a == true) {
if (line[i] == '=') { //after "="
i++;
break;
}
else {
i++;
}
}
for (i; line[i] != ' '; i++) {
output += line[i]; //put value to output
}
}
}
input.close();
return output;
}
pg_options.txt:
include_special_characters=true
include_upper_case=true
include_lower_case=true
include_numbers=true
digits=10
cout << read("digits") returns the error mentioned above.
Thank you for your comments. I solved this by editing the for loop:
string read(string value) {
ifstream input;
int olength;
string line = "", output = "";
size_t pos;
bool a = true;
int i = 0;
input.open("pg_options.txt");
if (!input.is_open()) {
cout << "pg_options.txt missing.";
return "error";
}
while (getline(input, line)) {
pos = line.find(value);
if (pos != string::npos) {
while (a == true) {
if (line[i] == '=') {
i++;
break;
}
else {
i++;
}
}
olength = line.length() - value.length() - 1;
for (int i2 = 0; i2 < olength; i2++) {
output += line[i];
i++;
}
}
}
input.close();
return output;
}
I'm supposed to make a function that receives two arguments: a sentence(std::string) and bunch of phrases(std::vector<std::string>>). Now for all words in a sentence that are contained in the vector, I need to make their palindrome, and stick them together, e.g. compile -> compileelipmoc.
I also need to make sure that input is available up until two ENTER's are pressed.
The problem occurs after calling the function, where I seem to get stuck in an infinite loop.
Why am I getting this infinite loop?
#include <iostream>
#include <vector>
#include <string>
typedef std::vector<std::string> Vektor;
typedef std::string String;
void ReverseString(String &s1)
{
char temp(0);
for(int i(0); i < s1.size()/2; i++) {
temp = s1.at(i);
s1.at(i) = s1.at(s1.length()-1-i);
s1.at(s1.length()-1-i) = temp;
}
}
void CreatePalindrome(String s, Vektor v)
{
bool white_space(true);
bool go_on(false);
String compare;
for(int i(0); i < s.size(); i++) {
for(;;) {
if(s.at(i) == '\n' || i == s.size()-1) {
go_on == true;
break;
}
compare+=s.at(i);
}
if(go_on) {
for(int j(0); j < v.size(); j++) {
if(compare == v.at(j)) {
ReverseString(v.at(j));
if(i != s.size()-1) v.at(j)+=' ';
s.insert(i, v.at(j));
}
}
}
compare.clear();
}
}
int main ()
{
String sentence, phrase;
Vektor v1;
char character(0);
std::cout << "Enter your sentence: ";
std::getline(std::cin, sentence);
std::cout << "Enter phrases: ";
for(;;) {
character = std::cin.get();
if(character == '\n') break;
for(;;) {
phrase.push_back(character);
character = std::cin.get();
if(character == '\n') break;
}
v1.push_back(phrase);
phrase.clear();
}
CreatePalindrome(sentence, v1);
std::cout << "After the transformation, the sentence is: " << sentence;
return 0;
}
for(;;) {
if(s.at(i) == '\n' || i == s.size()-1) {
go_on == true;
break;
}
compare+=s.at(i);
}
Your only way out is via that if, but you modify neither s nor i in the loop, so you'll never break!
I wanted to create function that returns true/false according if the input is a palindrome or not, when given abcddcba or aba it does not give true, but it should . plz help
bool checkPalindrome(char input[],int p=0) {
if(input[1]=='\0'){
return true;
}
if(sizeof(input)%2==0) {
int a = sizeof(input);
for(int i=0;i<(a/2);i++) {
if(input[0+i]==input[a-i-2]){
p++;
}
}
if(p==a/2){
return true;
} else{
return false;
}
}
else{
int a = sizeof(input);
for(int i=0;i<((a-1)/2);i++)
{
if(input[0+i]==input[a-i-2]){
p++;
}
}
if(p==(a-1)/2){
return true;
} else{
return false;
}
}
}
C++ style:
bool checkPalindrome(const std::string& str)
{
size_t len = str.size();
for (size_t i = 0; i < len/2; i++)
{
if (str[i] != str[len-1-i])
return false;
}
return true;
}
C style:
bool checkPalindrome(const char* str)
{
size_t len = str ? strlen(str) : 0;
for (size_t i = 0; i < len/2; i++)
{
if (str[i] != str[len-1-i])
return false;
}
return true;
}
In either case, you may need to evaluate (ask) if an empty string should be considered a palindrome or not.
your code is too long. There's a simple way: iterating once over the elements of the string comparing the n-1 with 0 and n-2 with 1 and so on.
If two characters are not identical return false otherwise continue checking:
bool checkPalindrome(char* str) {
const int size = strlen(str);
for(int i(0), j(size) - 1); i < size / 2; i++, j--)
if(str[i] != str[j])
return false;
return true;
}
int main(){
char* str = "level";
cout << checkPalindrome(str);
cout << endl << endl;
return 0;
}
C++ version based on strings and iterators (only for the sake of completeness, the other answers are already very nice) :
bool checkPalindrome(const string& str) {
for (auto p = str.begin(),q = str.end(); p!=q && p!=q+1; p++ )
if (*p!=*--q) // if char from front doesn't match char from rear ?
return false; // then it's not a palindrome !
return true;
}
Explanations: the iterator p starts at the front of the string and q at the rear (after the last char). p will advance and q will go backwards. If p reaches q or if p has passed q (special case if the word has an even length), it's over and we can conclude a palindrome. But if before, there is any difference between the char at p and the char preceeding q , it's not a palindrome.
Online demo, with little test suite
I've trying to grab a value from a file and see if it's an int or a string. if its a int it should go into the tempNum var if it's a string it should go into the tempString var. The rest of my code is written i just need to get that value into the correct variable.
while (!myFile.eof())
{
try
{
myFile >> tempNum;
}
catch (invalid_argument&)
{
myfile >> tempString;
}
}
Second attempt:
ifstream myFile;
myFile.open("data.txt");
while (myFile >> tempString)
{
tempNum = -1;
tempString = "-0";
bool isInteger = true;
for (int i = 0; i < tempString.length(); ++i)
{
if (!isdigit(tempString[i]))
{
isInteger = false;
break;
}
}
if (isInteger)
{
tempNum = stoi(tempString);
if (tempNum != -1)
cout << tempNum;
}
if (tempString != "-0")
cout << tempString;
}
system("pause");
return 0;
if (myFile >> tempNum) {
// it worked as an int
} else if (myfile >> tempString) {
// it worked as a string
}
When you read from a file, you should be reading it into a string:
while (fileVar >> myString)
{
// Do something with the string from file
}
So, you can test for each individual character to see what the whole is. Below is how to separate only the int's. Otherwise, if you want separate strings that contain only letters, then replace the "isdigit()" function with "isalpha()", or test for specific characters.
// Input validation (int)
bool isInteger = true;
for (int i = 0; i < myString.length(); ++i)
{
if (!isdigit(myString[i]))
{
isInteger = false;
break;
}
}
if (isInteger)
{
int myInt = stoi(myString);
}
I will be given string. I can remove only 1 element from it. After removing it if the new string becomes palindrome I have to print "Yes" otherwise "No".
For example, I am given a string "abdbca". Here I can remove 5th index 'c' and make it palindrome and i have to print "Yes". On the other hand if the string is something like "abcd" I can not make it palindrome by removing only one character. Hence I have to print "No".
I tried to do it but my code is not efficient enough. Can anybody please suggest me a efficient way to do it? I have to check strings of 10^5 length in less than 2.5 seconds.
the way I tried to do it is shown bellow :
#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#define REP(i,n) for(int i=0;i<n;i++)
#define MAX 100010
using namespace std;
bool isPalindrome(char abc[]){
int len = strlen(abc), lem = len/2;
for(int i=0,n=len-1;i<=lem;i++,n--) if(abc[i]!=abc[n]) return false;
return true;
}
int main()
{
int tc;
char str[MAX];
scanf("%d",&tc);
while(tc--){
scanf("%s", str);
int length = strlen(str), len = length - 1, z = length % 2, res = 0, ans = 0, b=0,lem = length / 2;
for(int i = 0;i<length;i++){
int n=0, m=1;
for(int x = 0, y = len;x<i && y!=i;x++,y--){
n++;
if(str[x]!=str[y]){
m=0; ++res;
break;
}
}
if(i>lem) for(int x=n,y=len-n-1;x<y;x++,y--){
if(str[x]!=str[y]){
m=0; ++res;
break;
}
}
else for(int x=n+1,y=len-n;x<y;x++,y--){
if(str[x]!=str[y]){
m=0; ++res;
break;
}
}
if(m==1) {printf("YES\n");b++;break;}
}
//if(length <= res) printf("NO\n");
if(b==0) printf("NO\n");
}
return 0;
}
Since you you only need to remove one character, you can do so in linear time by modifying palindrome checking. The idea is that you compare characters from the beginning to characters from the end and stop at the first mismatch. If you remove one character from the mismatching pair and get a palindrome, then return true, otherwise return false. I implemented the idea in C++ below.
#include<iostream>
#include<string>
using namespace std;
bool palindromeExists(string s)
{
int i = 0;
int j = s.length()-1;
while(i < j)
{
if(s[i] != s[j]) //first mismatch
break;
i++;
j--;
}
int tempj = j-1; //remove s[j]
int tempi = i;
while(tempi < tempj)
{
if(s[tempi] != s[tempj])
break;
tempi++;
tempj--;
}
if(tempi >= tempj) //palindrome found?
return true;
tempi = i+1; //remove s[i]
tempj = j;
while(tempi < tempj)
{
if(s[tempi] != s[tempj])
return false;
tempi++;
tempj--;
}
return true;
}
int main()
{
string s = "abca";
if(palindromeExists(s))
cout << "YES" << endl;
else
cout << "NO" << endl;
return 0;
}
This should return true if the string is already a palindrome, or if it can be a palindrome after the removal of one character. I hope I didn't miss any corner cases.
You can refer complete program in c++ here. Input the string to get the index of character to be removed. String reversal is performed in palim() function. It returns -1 if string is already palindrome.
#include <iostream>
#include <algorithm>
#include <string>
using namespace std;
bool palim(string s)
{
string s2;
s2=string(s.rbegin(),s.rend());
if(s2==s)
{
return true;
}
else
{
return false;
}
}
int check(string s)
{
int x;
if(s.length()%2==0)
{
for(int i=0,j=s.length()-1;i<s.length()/2,j>=s.length()/2;i++,j--)
{
if(s[i]!=s[j])
{
string s1=s;
s1.erase(j,1);
if(palim(s1))
{
x=j;
break;
}
else
{
string s1=s;
s1.erase(i,1);
if(palim(s1))
{
x=i;
break;
}
}
}
}
}
else
{
for(int i=0,j=s.length()-1;i<s.length()/2,j>s.length()/2;i++,j--)
{
if(s[i]!=s[j])
{
string s1=s;
s1.erase(j,1);
if(palim(s1))
{
x=j;
break;
}
else
{
string s1=s;
s1.erase(i,1);
if(palim(s1))
{
x=i;
break;
}
}
}
}
}
return x;
}
int main()
{
string s;
cin>>s;
if(palim(s))
{
cout<<"-1"<<endl;
}
else
{
cout<<check(s)<<endl;
}
return 0;
}
Similar to turingcomplete, but with sub functions:
bool isPalindrome(std::string::const_iterator& start, std::string::const_iterator& end)
{
while (start < end) {
--end;
if (*start != *end) {
return false;
}
++start;
}
return true;
}
bool test(const std::string& s)
{
auto start = s.begin();
auto end = s.end();
if (isPalindrome(start, end)) {
// If we remove the middle character of a palindrome,
// We still have a palindrome.
return true;
}
// Now test if there is a palindrome
// if we skip the mismatch char from the start or from the end.
auto start2 = start;
auto end2 = end;
++start2;
--end;
return isPalindrome(start, end) || isPalindrome(start2, end2);
}
Live example