add hw6 solution
This commit is contained in:
337
hws/inverse_word_search/main.cpp
Normal file
337
hws/inverse_word_search/main.cpp
Normal file
@@ -0,0 +1,337 @@
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <set>
|
||||
#include <algorithm>
|
||||
#include <cstdlib>
|
||||
#include <cctype>
|
||||
#include <climits>
|
||||
#include <utility>
|
||||
|
||||
using namespace std;
|
||||
|
||||
struct Placement {
|
||||
int r, c; // starting row and column
|
||||
int dr, dc; // direction increments
|
||||
string word; // the required word to place
|
||||
};
|
||||
|
||||
// Check if the given string s contains any forbidden word or its reverse.
|
||||
bool containsForbidden(const string &s, const vector<string> &forbWords) {
|
||||
for (size_t i = 0; i < forbWords.size(); i++) {
|
||||
if (s.find(forbWords[i]) != string::npos)
|
||||
return true;
|
||||
string rev = forbWords[i];
|
||||
reverse(rev.begin(), rev.end());
|
||||
if (s.find(rev) != string::npos)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Checks the full board for forbidden words in every contiguous line.
|
||||
bool fullForbiddenCheck(const vector< vector<char> > &board, const vector<string> &forbWords) {
|
||||
int H = board.size();
|
||||
if (H == 0) return false;
|
||||
int W = board[0].size();
|
||||
int dirs[8][2] = { {0,1}, {0,-1}, {1,0}, {-1,0}, {1,1}, {1,-1}, {-1,1}, {-1,-1} };
|
||||
for (int r = 0; r < H; r++) {
|
||||
for (int c = 0; c < W; c++) {
|
||||
for (int d = 0; d < 8; d++) {
|
||||
int dr = dirs[d][0], dc = dirs[d][1];
|
||||
string line = "";
|
||||
int rr = r, cc = c;
|
||||
while (rr >= 0 && rr < H && cc >= 0 && cc < W) {
|
||||
line.push_back(board[rr][cc]);
|
||||
if (containsForbidden(line, forbWords))
|
||||
return true;
|
||||
rr += dr;
|
||||
cc += dc;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Convert the board into a single string (rows separated by newline).
|
||||
string boardToString(const vector< vector<char> > &board) {
|
||||
string s = "";
|
||||
for (size_t i = 0; i < board.size(); i++) {
|
||||
for (size_t j = 0; j < board[i].size(); j++) {
|
||||
s.push_back(board[i][j]);
|
||||
}
|
||||
if (i < board.size()-1)
|
||||
s.push_back('\n');
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
// Given a newly assigned cell (r,c), check in all eight directions the contiguous block
|
||||
bool checkCellDirections(int r, int c, const vector< vector<char> > &board, const vector<string> &forbWords, int minForbidLen) {
|
||||
int H = board.size(), W = board[0].size();
|
||||
int dirs[8][2] = { {0,1}, {0,-1}, {1,0}, {-1,0}, {1,1}, {1,-1}, {-1,1}, {-1,-1} };
|
||||
for (int d = 0; d < 8; d++) {
|
||||
int dr = dirs[d][0], dc = dirs[d][1];
|
||||
int sr = r, sc = c;
|
||||
// Go backwards from (r,c)
|
||||
while (true) {
|
||||
int pr = sr - dr, pc = sc - dc;
|
||||
if (pr < 0 || pr >= H || pc < 0 || pc >= W) break;
|
||||
if (board[pr][pc] == '?') break;
|
||||
sr = pr; sc = pc;
|
||||
}
|
||||
// Build the contiguous block from (sr,sc)
|
||||
string block = "";
|
||||
int cr = sr, cc = sc;
|
||||
while (cr >= 0 && cr < H && cc >= 0 && cc < W) {
|
||||
if (board[cr][cc] == '?') break;
|
||||
block.push_back(board[cr][cc]);
|
||||
cr += dr; cc += dc;
|
||||
}
|
||||
if ((int)block.size() >= minForbidLen) {
|
||||
if (containsForbidden(block, forbWords))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Recursive function to fill free positions with letters
|
||||
bool fillFreePositions(vector<vector<char>> &board,
|
||||
vector<pair<int,int>> &freePositions,
|
||||
int index,
|
||||
const vector<string> &forbWords,
|
||||
int minForbidLen,
|
||||
set<string> &solutionSet,
|
||||
vector<string> &solutions,
|
||||
bool findOne,
|
||||
const string &outputFile) {
|
||||
// Base case: all free positions filled
|
||||
if (index == freePositions.size()) {
|
||||
// Check if the solution is valid
|
||||
if (!fullForbiddenCheck(board, forbWords)) {
|
||||
string solStr = boardToString(board);
|
||||
if (solutionSet.find(solStr) == solutionSet.end()) {
|
||||
solutionSet.insert(solStr);
|
||||
solutions.push_back(solStr);
|
||||
|
||||
// If we need just one solution, write it to file and exit
|
||||
if (findOne) {
|
||||
ofstream fout(outputFile.c_str());
|
||||
fout << "Board:" << endl;
|
||||
istringstream iss(solStr);
|
||||
string line;
|
||||
while(getline(iss, line))
|
||||
fout << " " << line << endl;
|
||||
fout.close();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false; // Continue searching for more solutions
|
||||
}
|
||||
|
||||
// Try each letter for the current free position
|
||||
int r = freePositions[index].first;
|
||||
int c = freePositions[index].second;
|
||||
|
||||
for (char ch = 'a'; ch <= 'z'; ch++) {
|
||||
board[r][c] = ch;
|
||||
|
||||
// Check if the new letter creates any forbidden words
|
||||
if (checkCellDirections(r, c, board, forbWords, minForbidLen)) {
|
||||
// Recursively fill the next position
|
||||
if (fillFreePositions(board, freePositions, index + 1, forbWords, minForbidLen,
|
||||
solutionSet, solutions, findOne, outputFile)) {
|
||||
return true; // Solution found and we only need one
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Backtrack: Reset the cell
|
||||
board[r][c] = '?';
|
||||
return false;
|
||||
}
|
||||
|
||||
// Recursive function to place required words
|
||||
bool placeRequiredWords(vector<vector<char>> &board,
|
||||
const vector<vector<Placement>> &placements,
|
||||
int wordIndex,
|
||||
const vector<string> &forbWords,
|
||||
int minForbidLen,
|
||||
set<string> &solutionSet,
|
||||
vector<string> &solutions,
|
||||
bool findOne,
|
||||
const string &outputFile) {
|
||||
// Base case: all required words placed
|
||||
if (wordIndex == placements.size()) {
|
||||
// Find free positions (cells marked with '?')
|
||||
vector<pair<int,int>> freePositions;
|
||||
for (int i = 0; i < board.size(); i++) {
|
||||
for (int j = 0; j < board[0].size(); j++) {
|
||||
if (board[i][j] == '?') {
|
||||
freePositions.push_back(make_pair(i, j));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Recursively fill free positions
|
||||
return fillFreePositions(board, freePositions, 0, forbWords, minForbidLen,
|
||||
solutionSet, solutions, findOne, outputFile);
|
||||
}
|
||||
|
||||
// Try each placement for the current word
|
||||
for (const Placement &p : placements[wordIndex]) {
|
||||
// Create a temporary copy of the board
|
||||
vector<vector<char>> boardCopy = board;
|
||||
bool conflict = false;
|
||||
|
||||
// Try to place the word
|
||||
for (size_t k = 0; k < p.word.size(); k++) {
|
||||
int r = p.r + p.dr * k;
|
||||
int c = p.c + p.dc * k;
|
||||
|
||||
if (boardCopy[r][c] == '?' || boardCopy[r][c] == p.word[k]) {
|
||||
boardCopy[r][c] = p.word[k];
|
||||
} else {
|
||||
conflict = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// If no conflict and no forbidden words formed, continue to the next word
|
||||
if (!conflict && !fullForbiddenCheck(boardCopy, forbWords)) {
|
||||
if (placeRequiredWords(boardCopy, placements, wordIndex + 1, forbWords, minForbidLen,
|
||||
solutionSet, solutions, findOne, outputFile)) {
|
||||
return true; // Solution found and we only need one
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false; // No solution found with any placement for this word
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
if (argc != 4) {
|
||||
cout << "Usage: " << argv[0] << " input.txt output.txt one_solution|all_solutions" << endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
string inputFile = argv[1];
|
||||
string outputFile = argv[2];
|
||||
string mode = argv[3];
|
||||
bool findOne = false;
|
||||
if (mode == "one_solution") {
|
||||
findOne = true;
|
||||
} else if (mode == "all_solutions") {
|
||||
findOne = false;
|
||||
} else {
|
||||
cout << "Invalid mode. Use one_solution or all_solutions." << endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
ifstream fin(inputFile.c_str());
|
||||
if (!fin) {
|
||||
cout << "Cannot open input file." << endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int width, height;
|
||||
fin >> width >> height;
|
||||
string dummy;
|
||||
getline(fin, dummy); // consume rest of first line
|
||||
|
||||
// Separate required and forbidden words.
|
||||
vector<string> reqWords;
|
||||
vector<string> forbWords;
|
||||
|
||||
while(getline(fin, dummy)) {
|
||||
if(dummy.size() == 0)
|
||||
continue;
|
||||
char sign = dummy[0];
|
||||
string word = "";
|
||||
int pos = 1;
|
||||
while (pos < dummy.size() && isspace(dummy[pos])) pos++;
|
||||
while (pos < dummy.size() && !isspace(dummy[pos])) {
|
||||
word.push_back(dummy[pos]);
|
||||
pos++;
|
||||
}
|
||||
if (sign == '+')
|
||||
reqWords.push_back(word);
|
||||
else if (sign == '-')
|
||||
forbWords.push_back(word);
|
||||
}
|
||||
fin.close();
|
||||
|
||||
// Precompute minimum forbidden word length.
|
||||
int minForbidLen = INT_MAX;
|
||||
for (size_t i = 0; i < forbWords.size(); i++) {
|
||||
if ((int)forbWords[i].size() < minForbidLen)
|
||||
minForbidLen = forbWords[i].size();
|
||||
}
|
||||
if (forbWords.empty())
|
||||
minForbidLen = 27; // no forbidden word; no check needed
|
||||
|
||||
// Precompute all placements for each required word.
|
||||
vector<vector<Placement>> placements;
|
||||
placements.resize(reqWords.size());
|
||||
int directions[8][2] = { {0,1}, {0,-1}, {1,0}, {-1,0}, {1,1}, {1,-1}, {-1,1}, {-1,-1} };
|
||||
|
||||
for (size_t w = 0; w < reqWords.size(); w++) {
|
||||
string word = reqWords[w];
|
||||
for (int r = 0; r < height; r++) {
|
||||
for (int c = 0; c < width; c++) {
|
||||
for (int d = 0; d < 8; d++) {
|
||||
int dr = directions[d][0], dc = directions[d][1];
|
||||
int end_r = r + dr * (word.size() - 1);
|
||||
int end_c = c + dc * (word.size() - 1);
|
||||
if (end_r < 0 || end_r >= height || end_c < 0 || end_c >= width)
|
||||
continue;
|
||||
Placement p;
|
||||
p.r = r; p.c = c; p.dr = dr; p.dc = dc; p.word = word;
|
||||
placements[w].push_back(p);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (placements[w].empty()) {
|
||||
ofstream fout(outputFile.c_str());
|
||||
fout << "No solutions found" << endl;
|
||||
fout.close();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Use a set to avoid duplicate solutions.
|
||||
set<string> solutionSet;
|
||||
vector<string> solutions;
|
||||
|
||||
// Prepare the base board filled with '?'.
|
||||
vector<vector<char>> board(height, vector<char>(width, '?'));
|
||||
|
||||
// Call the recursive function to place required words
|
||||
placeRequiredWords(board, placements, 0, forbWords, minForbidLen,
|
||||
solutionSet, solutions, findOne, outputFile);
|
||||
|
||||
// Write out all found solutions (if findOne is true, we've already written the solution).
|
||||
if (!findOne || solutions.empty()) {
|
||||
ofstream fout(outputFile.c_str());
|
||||
if (solutions.empty()) {
|
||||
fout << "No solutions found" << endl;
|
||||
} else {
|
||||
fout << solutions.size() << " solution(s)" << endl;
|
||||
for (size_t s = 0; s < solutions.size(); s++) {
|
||||
fout << "Board:" << endl;
|
||||
istringstream iss(solutions[s]);
|
||||
string line;
|
||||
while(getline(iss, line))
|
||||
fout << " " << line << endl;
|
||||
}
|
||||
}
|
||||
fout.close();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user